diff options
Diffstat (limited to 'V8Binding/v8/V8NPObject.cpp')
-rw-r--r-- | V8Binding/v8/V8NPObject.cpp | 369 |
1 files changed, 369 insertions, 0 deletions
diff --git a/V8Binding/v8/V8NPObject.cpp b/V8Binding/v8/V8NPObject.cpp new file mode 100644 index 0000000..60e66a9 --- /dev/null +++ b/V8Binding/v8/V8NPObject.cpp @@ -0,0 +1,369 @@ +// Copyright (c) 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "config.h" + +#include "v8_custom.h" +#include "v8_helpers.h" +#include "V8NPObject.h" +#include "V8NPUtils.h" +#include "NPV8Object.h" +#include "npruntime_priv.h" +#include "v8_proxy.h" +#include "dom_wrapper_map.h" +#include "HTMLPlugInElement.h" +#include "V8HTMLAppletElement.h" +#include "V8HTMLEmbedElement.h" +#include "V8HTMLObjectElement.h" + +using namespace WebCore; + +enum InvokeFunctionType { + INVOKE_METHOD = 1, + INVOKE_DEFAULT = 2 +}; + +// TODO(mbelshe): need comments. +// Params: holder could be HTMLEmbedElement or NPObject +static v8::Handle<v8::Value> NPObjectInvokeImpl(const v8::Arguments& args, InvokeFunctionType funcId) +{ + NPObject* npobject; + + // These three types are subtypes of HTMLPlugInElement. + if (V8HTMLAppletElement::HasInstance(args.Holder()) || + V8HTMLEmbedElement::HasInstance(args.Holder()) || + V8HTMLObjectElement::HasInstance(args.Holder())) { + // The holder object is a subtype of HTMLPlugInElement. + HTMLPlugInElement* imp = V8Proxy::DOMWrapperToNode<HTMLPlugInElement>(args.Holder()); + ScriptInstance scriptInstance = imp->getInstance(); + if (scriptInstance) + npobject = V8Proxy::ToNativeObject<NPObject>(V8ClassIndex::NPOBJECT, scriptInstance->instance()); + else + npobject = NULL; + } else { + // The holder object is not a subtype of HTMLPlugInElement, it + // must be an NPObject which has three internal fields. + if (args.Holder()->InternalFieldCount() != V8Custom::kNPObjectInternalFieldCount) { + V8Proxy::ThrowError(V8Proxy::REFERENCE_ERROR, "NPMethod called on non-NPObject"); + return v8::Undefined(); + } + npobject = V8Proxy::ToNativeObject<NPObject>(V8ClassIndex::NPOBJECT, args.Holder()); + } + + // Verify that our wrapper wasn't using a NPObject which + // has already been deleted. + if (!npobject || !_NPN_IsAlive(npobject)) { + V8Proxy::ThrowError(V8Proxy::REFERENCE_ERROR, "NPObject deleted"); + return v8::Undefined(); + } + + // wrap up parameters + int argc = args.Length(); + NPVariant* npArgs = new NPVariant[argc]; + + for (int i = 0; i < argc; i++) + convertV8ObjectToNPVariant(args[i], npobject, &npArgs[i]); + + NPVariant result; + VOID_TO_NPVARIANT(result); + + switch (funcId) { + case INVOKE_METHOD: + if (npobject->_class->invoke) { + v8::Handle<v8::String> function_name(v8::String::Cast(*args.Data())); + NPIdentifier ident = getStringIdentifier(function_name); + npobject->_class->invoke(npobject, ident, npArgs, argc, &result); + } + break; + case INVOKE_DEFAULT: + if (npobject->_class->invokeDefault) + npobject->_class->invokeDefault(npobject, npArgs, argc, &result); + break; + default: + break; + } + + for (int i=0; i < argc; i++) + NPN_ReleaseVariantValue(&npArgs[i]); + delete[] npArgs; + + // unwrap return values + v8::Handle<v8::Value> rv = convertNPVariantToV8Object(&result, npobject); + NPN_ReleaseVariantValue(&result); + + return rv; +} + + +v8::Handle<v8::Value> NPObjectMethodHandler(const v8::Arguments& args) +{ + return NPObjectInvokeImpl(args, INVOKE_METHOD); +} + + +v8::Handle<v8::Value> NPObjectInvokeDefaultHandler(const v8::Arguments& args) +{ + return NPObjectInvokeImpl(args, INVOKE_DEFAULT); +} + + +static void WeakTemplateCallback(v8::Persistent<v8::Value> obj, void* param); + +// NPIdentifier is PrivateIdentifier*. +static WeakReferenceMap<PrivateIdentifier, v8::FunctionTemplate> \ + static_template_map(&WeakTemplateCallback); + +static void WeakTemplateCallback(v8::Persistent<v8::Value> obj, void* param) +{ + PrivateIdentifier* iden = static_cast<PrivateIdentifier*>(param); + ASSERT(iden != NULL); + ASSERT(static_template_map.contains(iden)); + + static_template_map.forget(iden); +} + + +static v8::Handle<v8::Value> NPObjectGetProperty(v8::Local<v8::Object> self, + NPIdentifier ident, + v8::Local<v8::Value> key) +{ + NPObject* npobject = V8Proxy::ToNativeObject<NPObject>(V8ClassIndex::NPOBJECT, self); + + // Verify that our wrapper wasn't using a NPObject which + // has already been deleted. + if (!npobject || !_NPN_IsAlive(npobject)) { + V8Proxy::ThrowError(V8Proxy::REFERENCE_ERROR, "NPObject deleted"); + return v8::Handle<v8::Value>(); + } + + if (npobject->_class->hasProperty && + npobject->_class->hasProperty(npobject, ident) && + npobject->_class->getProperty) { + + NPVariant result; + VOID_TO_NPVARIANT(result); + if (!npobject->_class->getProperty(npobject, ident, &result)) + return v8::Handle<v8::Value>(); + + v8::Handle<v8::Value> rv = convertNPVariantToV8Object(&result, npobject); + NPN_ReleaseVariantValue(&result); + return rv; + } else if (key->IsString() && npobject->_class->hasMethod && npobject->_class->hasMethod(npobject, ident)) { + PrivateIdentifier* id = static_cast<PrivateIdentifier*>(ident); + v8::Persistent<v8::FunctionTemplate> desc = static_template_map.get(id); + // Cache templates using identifier as the key. + if (desc.IsEmpty()) { + // Create a new template + v8::Local<v8::FunctionTemplate> temp = v8::FunctionTemplate::New(); + temp->SetCallHandler(NPObjectMethodHandler, key); + desc = v8::Persistent<v8::FunctionTemplate>::New(temp); + static_template_map.set(id, desc); + } + + // FunctionTemplate caches function for each context. + v8::Local<v8::Function> func = desc->GetFunction(); + func->SetName(v8::Handle<v8::String>::Cast(key)); + return func; + } + + return v8::Handle<v8::Value>(); +} + +v8::Handle<v8::Value> NPObjectNamedPropertyGetter(v8::Local<v8::String> name, + const v8::AccessorInfo& info) +{ + NPIdentifier ident = getStringIdentifier(name); + return NPObjectGetProperty(info.Holder(), ident, name); +} + +v8::Handle<v8::Value> NPObjectIndexedPropertyGetter(uint32_t index, + const v8::AccessorInfo& info) +{ + NPIdentifier ident = NPN_GetIntIdentifier(index); + return NPObjectGetProperty(info.Holder(), ident, v8::Number::New(index)); +} + +v8::Handle<v8::Value> NPObjectGetNamedProperty(v8::Local<v8::Object> self, + v8::Local<v8::String> name) +{ + NPIdentifier ident = getStringIdentifier(name); + return NPObjectGetProperty(self, ident, name); +} + +v8::Handle<v8::Value> NPObjectGetIndexedProperty(v8::Local<v8::Object> self, + uint32_t index) +{ + NPIdentifier ident = NPN_GetIntIdentifier(index); + return NPObjectGetProperty(self, ident, v8::Number::New(index)); +} + +static v8::Handle<v8::Value> NPObjectSetProperty(v8::Local<v8::Object> self, + NPIdentifier ident, + v8::Local<v8::Value> value) +{ + NPObject* npobject = V8Proxy::ToNativeObject<NPObject>(V8ClassIndex::NPOBJECT, self); + + // Verify that our wrapper wasn't using a NPObject which + // has already been deleted. + if (!npobject || !_NPN_IsAlive(npobject)) { + V8Proxy::ThrowError(V8Proxy::REFERENCE_ERROR, "NPObject deleted"); + return value; // intercepted, but an exception was thrown + } + + if (npobject->_class->hasProperty && + npobject->_class->hasProperty(npobject, ident) && + npobject->_class->setProperty) { + + NPVariant npvalue; + VOID_TO_NPVARIANT(npvalue); + convertV8ObjectToNPVariant(value, npobject, &npvalue); + bool succ = npobject->_class->setProperty(npobject, ident, &npvalue); + NPN_ReleaseVariantValue(&npvalue); + if (succ) + return value; // intercept the call + } + return v8::Local<v8::Value>(); // do not intercept the call +} + + +v8::Handle<v8::Value> NPObjectNamedPropertySetter(v8::Local<v8::String> name, + v8::Local<v8::Value> value, + const v8::AccessorInfo& info) +{ + NPIdentifier ident = getStringIdentifier(name); + return NPObjectSetProperty(info.Holder(), ident, value); +} + + +v8::Handle<v8::Value> NPObjectIndexedPropertySetter(uint32_t index, + v8::Local<v8::Value> value, + const v8::AccessorInfo& info) +{ + NPIdentifier ident = NPN_GetIntIdentifier(index); + return NPObjectSetProperty(info.Holder(), ident, value); +} + +v8::Handle<v8::Value> NPObjectSetNamedProperty(v8::Local<v8::Object> self, + v8::Local<v8::String> name, + v8::Local<v8::Value> value) +{ + NPIdentifier ident = getStringIdentifier(name); + return NPObjectSetProperty(self, ident, value); +} + +v8::Handle<v8::Value> NPObjectSetIndexedProperty(v8::Local<v8::Object> self, + uint32_t index, + v8::Local<v8::Value> value) +{ + NPIdentifier ident = NPN_GetIntIdentifier(index); + return NPObjectSetProperty(self, ident, value); +} + + +static void WeakNPObjectCallback(v8::Persistent<v8::Value> obj, void* param); + +static DOMWrapperMap<NPObject> staticNpobjectMap(&WeakNPObjectCallback); + +static void WeakNPObjectCallback(v8::Persistent<v8::Value> obj, void* param) +{ + NPObject* npobject = static_cast<NPObject*>(param); + ASSERT(staticNpobjectMap.contains(npobject)); + ASSERT(npobject != NULL); + + // Must remove from our map before calling NPN_ReleaseObject(). + // NPN_ReleaseObject can call ForgetV8ObjectForNPObject, which + // uses the table as well. + staticNpobjectMap.forget(npobject); + + if (_NPN_IsAlive(npobject)) + NPN_ReleaseObject(npobject); +} + + +v8::Local<v8::Object> CreateV8ObjectForNPObject(NPObject* object, NPObject* root) +{ + static v8::Persistent<v8::FunctionTemplate> npObjectDesc; + + ASSERT(v8::Context::InContext()); + + // If this is a v8 object, just return it. + if (object->_class == npScriptObjectClass) { + V8NPObject* v8npobject = reinterpret_cast<V8NPObject*>(object); + return v8::Local<v8::Object>::New(v8npobject->v8Object); + } + + // If we've already wrapped this object, just return it. + if (staticNpobjectMap.contains(object)) + return v8::Local<v8::Object>::New(staticNpobjectMap.get(object)); + + // TODO: we should create a Wrapper type as a subclass of JSObject. + // It has two internal fields, field 0 is the wrapped pointer, + // and field 1 is the type. There should be an api function that + // returns unused type id. + // The same Wrapper type can be used by DOM bindings. + if (npObjectDesc.IsEmpty()) { + npObjectDesc = v8::Persistent<v8::FunctionTemplate>::New(v8::FunctionTemplate::New()); + npObjectDesc->InstanceTemplate()->SetInternalFieldCount(V8Custom::kNPObjectInternalFieldCount); + npObjectDesc->InstanceTemplate()->SetNamedPropertyHandler(NPObjectNamedPropertyGetter, NPObjectNamedPropertySetter); + npObjectDesc->InstanceTemplate()->SetIndexedPropertyHandler(NPObjectIndexedPropertyGetter, NPObjectIndexedPropertySetter); + npObjectDesc->InstanceTemplate()->SetCallAsFunctionHandler(NPObjectInvokeDefaultHandler); + } + + v8::Handle<v8::Function> func = npObjectDesc->GetFunction(); + v8::Local<v8::Object> value = SafeAllocation::NewInstance(func); + + // If we were unable to allocate the instance we avoid wrapping + // and registering the NP object. + if (value.IsEmpty()) + return value; + + WrapNPObject(value, object); + + // KJS retains the object as part of its wrapper (see Bindings::CInstance) + NPN_RetainObject(object); + + _NPN_RegisterObject(object, root); + + // Maintain a weak pointer for v8 so we can cleanup the object. + v8::Persistent<v8::Object> weakRef = v8::Persistent<v8::Object>::New(value); + staticNpobjectMap.set(object, weakRef); + + return value; +} + +void ForgetV8ObjectForNPObject(NPObject* object) +{ + if (staticNpobjectMap.contains(object)) { + v8::HandleScope scope; + v8::Persistent<v8::Object> handle(staticNpobjectMap.get(object)); + WebCore::V8Proxy::SetDOMWrapper(handle, WebCore::V8ClassIndex::NPOBJECT, NULL); + staticNpobjectMap.forget(object); + NPN_ReleaseObject(object); + } +} |