/* * Copyright 2009, 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. */ // Code relating to interoperation of V8 JavaScript engine with NPAPI. // Tests are in o3d/tests/selenium/tests/v8.html. They can be run // by opening the web page in a browser or as part of the selenium tests. #include <npapi.h> #include <sstream> #include <vector> #include "plugin/cross/np_v8_bridge.h" using v8::AccessorInfo; using v8::Arguments; using v8::Array; using v8::Context; using v8::DontDelete; using v8::DontEnum; using v8::External; using v8::Function; using v8::FunctionTemplate; using v8::HandleScope; using v8::Int32; using v8::Integer; using v8::Local; using v8::Message; using v8::Null; using v8::Number; using v8::Object; using v8::ObjectTemplate; using v8::Persistent; using v8::PropertyAttribute; using v8::ReadOnly; using v8::Script; using v8::TryCatch; using v8::Undefined; using v8::Value; namespace o3d { // Only used during debugging. Type "o3d::DebugV8String(a.val_)" in the // watch window to get the string representation of a V8 object. const char* DebugV8String(Value* value) { static char buffer[4096]; if (value == NULL) { ::base::snprintf(buffer, sizeof(buffer), "<null>"); } else { value->ToString()->WriteUtf8(buffer); } return buffer; } namespace { // The indices of the internal fields of a V8 proxy for an NPObject. enum { // Pointer to the bridge that created the proxy. V8_NP_OBJECT_BRIDGE, // Pointer to the wrapped NPObject. V8_NP_OBJECT_WRAPPED, V8_NP_OBJECT_NUM_INTERNAL_FIELDS }; // The name of the "hidden" property in a V8 non-proxy object that contains // an External that points to the NPObject proxy for it. The property does // not exist if there is no associated NPObject proxy. const char* const kInternalProperty = "internal_property_"; // Convert an NPIdentifier (NULL, string or integer) to a V8 value. Local<Value> NPToV8Identifier(NPIdentifier np_identifier) { if (np_identifier == NULL) { return Local<Value>(); } else if (NPN_IdentifierIsString(np_identifier)) { NPUTF8* utf8_name = NPN_UTF8FromIdentifier(np_identifier); Local<v8::String> v8_identifier = v8::String::New(utf8_name); NPN_MemFree(utf8_name); return v8_identifier; } else { return Integer::New(NPN_IntFromIdentifier(np_identifier)); } } // Convert a V8 value (empty, string or integer) into an NPIdentifier. NPIdentifier V8ToNPIdentifier(v8::Handle<Value> v8_identifier) { if (v8_identifier.IsEmpty()) { return NULL; } else if (v8_identifier->IsNumber()) { return NPN_GetIntIdentifier(v8_identifier->Int32Value()); } else if (v8_identifier->IsString()) { return NPN_GetStringIdentifier( *v8::String::Utf8Value(v8_identifier->ToString())); } else { return NULL; } } } // namespace anonymous // The class of NPObject proxies that wrap V8 objects. These field the NPAPI // functions and translate them into V8 calls. class NPV8Object : public NPObject { public: static NPObjectPtr<NPV8Object> Create(NPV8Bridge* bridge, Local<Object> v8_object) { NPObjectPtr<NPV8Object> np_object = NPObjectPtr<NPV8Object>::AttachToReturned( static_cast<NPV8Object*>(NPN_CreateObject(bridge->npp(), &np_class_))); np_object->v8_object_ = Persistent<Object>::New(v8_object); np_object->bridge_ = bridge; return np_object; } v8::Handle<Object> v8_object() const { return v8_object_; } // Drop references between NPObject and V8 object. Must be called before the // NPObject is destroyed so V8 can garbage collect the associated V8 object. void UnlinkFromV8() { HandleScope handle_scope; if (!v8_object_.IsEmpty()) { v8_object_->DeleteHiddenValue(v8::String::NewSymbol(kInternalProperty)); v8_object_.Dispose(); v8_object_.Clear(); } } static NPClass np_class_; private: NPV8Object() : bridge_(NULL) { } static NPObject* Allocate(NPP npp, NPClass* np_class) { NPV8Object* np_v8_object = new NPV8Object(); np_v8_object->bridge_ = NULL; return np_v8_object; } static void Deallocate(NPObject* np_object) { NPV8Object* np_v8_object = static_cast<NPV8Object*> (np_object); // Uncomment this line to see objects with a non-zero reference // count being deallocated. For example, Firefox does this when unloading // the plugin. // DCHECK_EQ(0, np_v8_object_map->referenceCount); np_v8_object->UnlinkFromV8(); delete np_v8_object; } static void Invalidate(NPObject* np_object) { NPV8Object* np_v8_object = static_cast<NPV8Object*> (np_object); np_v8_object->bridge_ = NULL; np_v8_object->UnlinkFromV8(); } static bool HasMethod(NPObject* np_object, NPIdentifier np_name) { NPV8Object* np_v8_object = static_cast<NPV8Object*> (np_object); NPV8Bridge* bridge = np_v8_object->bridge_; if (bridge == NULL) return false; HandleScope handle_scope; Context::Scope scope(bridge->script_context()); TryCatch tryCatch; v8::Handle<Object> v8_object = np_v8_object->v8_object_; if (v8_object.IsEmpty()) return false; Local<Value> v8_name = NPToV8Identifier(np_name); Local<Value> value = v8_object->Get(v8_name); if (tryCatch.HasCaught()) { bridge->ReportV8Exception(tryCatch); return false; } // Returns true iff the object has a property with the given name and // the object assigned to the property is a function. This works for V8 // functions and assigned browser JavaScript functions (because their // proxies are created from FunctionTemplates so V8 considers them to be // functions). return !value.IsEmpty() && value->IsFunction(); } // Called when a method is invoked through "obj.m(...)". static bool Invoke(NPObject* np_object, NPIdentifier np_name, const NPVariant* np_args, uint32_t numArgs, NPVariant* result) { // This works around a bug in Chrome: // http://code.google.com/p/chromium/issues/detail?id=5110 // NPN_InvokeDefault is transformed into a call to Invoke on the plugin with // a null method name identifier. if (np_name == NULL) { return InvokeDefault(np_object, np_args, numArgs, result); } NPV8Object* np_v8_object = static_cast<NPV8Object*> (np_object); NPV8Bridge* bridge = np_v8_object->bridge_; if (bridge == NULL) return false; HandleScope handle_scope; Context::Scope scope(bridge->script_context()); TryCatch tryCatch; v8::Handle<Object> v8_object = np_v8_object->v8_object_; if (v8_object.IsEmpty()) return false; Local<Value> v8_name = NPToV8Identifier(np_name); Local<Value> value = v8_object->Get(v8_name); if (value.IsEmpty() || !value->IsFunction()) return false; Local<Function> function = Local<Function>::Cast(value); std::vector<v8::Handle<Value> > v8_args(numArgs); for (uint32_t i = 0; i != numArgs; ++i) { v8_args[i] = bridge->NPToV8Variant(np_args[i]); } *result = bridge->V8ToNPVariant( function->Call(v8_object, numArgs, numArgs == 0 ? NULL : &v8_args.front())); if (tryCatch.HasCaught()) { bridge->ReportV8Exception(tryCatch); return false; } return true; } // Called when an object is called as a function "f(...)". static bool InvokeDefault(NPObject* np_object, const NPVariant* np_args, uint32_t numArgs, NPVariant* result) { NPV8Object* np_v8_object = static_cast<NPV8Object*> (np_object); NPV8Bridge* bridge = np_v8_object->bridge_; if (bridge == NULL) return false; HandleScope handle_scope; Context::Scope scope(bridge->script_context()); TryCatch tryCatch; v8::Handle<Object> v8_object = np_v8_object->v8_object_; if (v8_object.IsEmpty()) return false; if (!v8_object->IsFunction()) return false; v8::Handle<Function> function = v8::Handle<Function>::Cast(v8_object); std::vector<v8::Handle<Value> > v8_args(numArgs); for (uint32_t i = 0; i != numArgs; ++i) { v8_args[i] = bridge->NPToV8Variant(np_args[i]); } *result = bridge->V8ToNPVariant( function->Call(v8_object, numArgs, numArgs == 0 ? NULL : &v8_args.front())); if (tryCatch.HasCaught()) { bridge->ReportV8Exception(tryCatch); return false; } return true; } // Called when an object is called as a constructor "new C(...)". static bool Construct(NPObject* np_object, const NPVariant* np_args, uint32_t numArgs, NPVariant* result) { NPV8Object* np_v8_object = static_cast<NPV8Object*> (np_object); NPV8Bridge* bridge = np_v8_object->bridge_; if (bridge == NULL) return false; HandleScope handle_scope; Context::Scope scope(bridge->script_context()); TryCatch tryCatch; v8::Handle<Object> v8_object = np_v8_object->v8_object_; if (v8_object.IsEmpty()) return false; if (!v8_object->IsFunction()) return false; v8::Handle<Function> function = v8::Handle<Function>::Cast(v8_object); std::vector<v8::Handle<Value> > v8_args(numArgs); for (uint32_t i = 0; i != numArgs; ++i) { v8_args[i] = bridge->NPToV8Variant(np_args[i]); } Local<Object> v8_result = function->NewInstance( numArgs, numArgs == 0 ? NULL : &v8_args.front()); if (v8_result.IsEmpty()) return false; *result = bridge->V8ToNPVariant(v8_result); if (tryCatch.HasCaught()) { bridge->ReportV8Exception(tryCatch); return false; } return true; } static bool HasProperty(NPObject* np_object, NPIdentifier np_name) { NPV8Object* np_v8_object = static_cast<NPV8Object*> (np_object); NPV8Bridge* bridge = np_v8_object->bridge_; if (bridge == NULL) return false; HandleScope handle_scope; Context::Scope scope(bridge->script_context()); v8::Handle<Object> v8_object = np_v8_object->v8_object_; if (v8_object.IsEmpty()) return false; // This is a better approach than the one below. It allows functions // to be retreived as first class objects. Unfortunately we can't // support this yet because of a Chrome bug: // http://code.google.com/p/chromium/issues/detail?id=5742 // if (NPN_IdentifierIsString(np_name)) { // Local<v8::String> v8_name = Local<v8::String>::Cast( // NPToV8Identifier(np_name)); // return v8_object->Has(v8_name); // } else { // return v8_object->Has(NPN_IntFromIdentifier(np_name)); // } // Instead hide properties with function type. This ensures that Chrome // will invoke them with Invoke rather than InvokeDefault. The problem // with InvokeDefault is it doesn't tell us what "this" should be // bound to, whereas Invoke does. Local<Value> v8_name = NPToV8Identifier(np_name); if (NPN_IdentifierIsString(np_name)) { if (!v8_object->Has(v8_name->ToString())) { return false; } } else { if (!v8_object->Has(NPN_IntFromIdentifier(np_name))) { return false; } } Local<Value> v8_property_value = v8_object->Get(v8_name); if (v8_property_value->IsFunction()) { return false; } return true; } static bool GetProperty(NPObject* np_object, NPIdentifier np_name, NPVariant* result) { NPV8Object* np_v8_object = static_cast<NPV8Object*> (np_object); NPV8Bridge* bridge = np_v8_object->bridge_; if (bridge == NULL) return false; HandleScope handle_scope; Context::Scope scope(bridge->script_context()); TryCatch tryCatch; v8::Handle<Object> v8_object = np_v8_object->v8_object_; if (v8_object.IsEmpty()) return false; Local<Value> v8_name = NPToV8Identifier(np_name); Local<Value> v8_property_value = v8_object->Get(v8_name); if (tryCatch.HasCaught()) { bridge->ReportV8Exception(tryCatch); return false; } // See comment in HasProperty. Do not return properties that are // functions. It will prevent Chrome from invoking them as methods. if (v8_property_value.IsEmpty() || v8_property_value->IsFunction()) return false; *result = bridge->V8ToNPVariant(v8_property_value); return true; } static bool SetProperty(NPObject* np_object, NPIdentifier np_name, const NPVariant* np_value) { NPV8Object* np_v8_object = static_cast<NPV8Object*> (np_object); NPV8Bridge* bridge = np_v8_object->bridge_; if (bridge == NULL) return false; HandleScope handle_scope; Context::Scope scope(bridge->script_context()); TryCatch tryCatch; v8::Handle<Object> v8_object = np_v8_object->v8_object_; if (v8_object.IsEmpty()) return false; Local<Value> v8_name = NPToV8Identifier(np_name); bool success = v8_object->Set(v8_name, bridge->NPToV8Variant(*np_value)); if (tryCatch.HasCaught()) { bridge->ReportV8Exception(tryCatch); return false; } return success; } static bool RemoveProperty(NPObject* np_object, NPIdentifier np_name) { NPV8Object* np_v8_object = static_cast<NPV8Object*> (np_object); NPV8Bridge* bridge = np_v8_object->bridge_; if (bridge == NULL) return false; HandleScope handle_scope; Context::Scope scope(bridge->script_context()); TryCatch tryCatch; v8::Handle<Object> v8_object = np_v8_object->v8_object_; if (v8_object.IsEmpty()) return false; bool success; if (NPN_IdentifierIsString(np_name)) { NPUTF8* utf8_name = NPN_UTF8FromIdentifier(np_name); Local<v8::String> v8_name = v8::String::New(utf8_name); NPN_MemFree(utf8_name); success = v8_object->Delete(v8_name); } else { success = v8_object->Delete(NPN_IntFromIdentifier(np_name)); } if (tryCatch.HasCaught()) { bridge->ReportV8Exception(tryCatch); return false; } return success; } static bool Enumerate(NPObject* np_object, NPIdentifier** np_names, uint32_t* numNames) { NPV8Object* np_v8_object = static_cast<NPV8Object*> (np_object); NPV8Bridge* bridge = np_v8_object->bridge_; if (bridge == NULL) return false; HandleScope handle_scope; Context::Scope scope(bridge->script_context()); v8::Handle<Object> v8_object = np_v8_object->v8_object_; if (v8_object.IsEmpty()) return false; Local<Array> v8_names = v8_object->GetPropertyNames(); // Due to a bug in Chrome, need to filter out any properties that // are functions. See comment in HasProperty. int num_non_function_properties = 0; int length = v8_names->Length(); for (int i = 0; i < length; ++i) { Local<Value> v8_property_value = v8_object->Get(v8_names->Get(Int32::New(i))); if (!v8_property_value->IsFunction()) { ++num_non_function_properties; } } *numNames = num_non_function_properties; *np_names = static_cast<NPIdentifier*> ( NPN_MemAlloc(num_non_function_properties * sizeof(NPIdentifier))); int j = 0; for (uint32_t i = 0; i != v8_names->Length(); ++i) { Local<Value> v8_name = v8_names->Get(Int32::New(i)); Local<Value> v8_property_value = v8_object->Get(v8_name); if (!v8_property_value->IsFunction()) { (*np_names)[j++] = V8ToNPIdentifier(v8_name); } } return true; } NPV8Bridge* bridge_; AutoV8Persistent<Object> v8_object_; DISALLOW_COPY_AND_ASSIGN(NPV8Object); }; NPClass NPV8Object::np_class_ = { NP_CLASS_STRUCT_VERSION, Allocate, Deallocate, Invalidate, HasMethod, Invoke, InvokeDefault, HasProperty, GetProperty, SetProperty, RemoveProperty, Enumerate, Construct }; NPV8Bridge::NPV8Bridge(ServiceLocator* service_locator, NPP npp) : service_locator_(service_locator), error_status_(service_locator), npp_(npp) { np_name_identifier_ = NPN_GetStringIdentifier("name"); np_call_identifier_ = NPN_GetStringIdentifier("call"); np_length_identifier_ = NPN_GetStringIdentifier("length"); np_proxy_identifier_ = NPN_GetStringIdentifier("npv8_proxy_"); } NPV8Bridge::~NPV8Bridge() { // Do not call weak reference callback after the bridge is destroyed // because the callbacks assume it exists. The only purpose of the callback // is to remove the corresponding object entry from the NP-V8 object map // and its about to get cleared anyway. for (NPV8ObjectMap::iterator it = np_v8_object_map_.begin(); it != np_v8_object_map_.end(); ++it) { it->second.ClearWeak(); } } NPObjectPtr<NPObject> NPV8Bridge::NPEvaluateObject(const char* script) { NPString np_script = { script, strlen(script) }; NPVariant np_variant; NPObjectPtr<NPObject> np_result; if (NPN_Evaluate(npp_, global_np_object_.Get(), &np_script, &np_variant)) { if (NPVARIANT_IS_OBJECT(np_variant)) { np_result = NPObjectPtr<NPObject>(NPVARIANT_TO_OBJECT(np_variant)); } NPN_ReleaseVariantValue(&np_variant); } return np_result; } namespace { // Create code that looks like this: // (function(func, protoArray) { // return function() { // switch (arguments.length) { // case 0: // return func.call(this); // case 1: // return func.call(this, // arguments[0]); // case 2: // return func.call(this, // arguments[0], // arguments[1]); // ... // default: // var args = protoArray.slice(); // for (var i = 0; i < arguments.length; ++i) { // args[i] = arguments[i]; // } // return func.apply(this, args); // } // }; // }) String MakeWrapFunctionScript() { std::ostringstream code; code << "(function(func, protoArray) {"; code << " return function() {"; code << " switch (arguments.length) {"; for (int i = 0; i <= 10; ++i) { code << " case " << i << ": return func.call(this"; for (int j = 0; j < i; ++j) { code << ", arguments[" << j << "]"; } code << ");"; } code << " default:"; code << " var args = protoArray.slice();"; code << " for (var i = 0; i < arguments.length; ++i) {"; code << " args.push(arguments[i]);"; code << " }"; code << " return func.apply(this, args);"; code << " }"; code << " };"; code << "})"; return code.str(); } } // namespace anonymous void NPV8Bridge::Initialize(const NPObjectPtr<NPObject>& global_np_object) { HandleScope handle_scope; global_np_object_ = global_np_object; // This template is used for V8 proxies of NPObjects. v8_np_constructor_template_ = Persistent<FunctionTemplate>::New( FunctionTemplate::New()); InitializeV8ObjectTemplate(v8_np_constructor_template_->InstanceTemplate()); // This template is used for the global V8 object. Local<FunctionTemplate> v8_global_template = FunctionTemplate::New(); InitializeV8ObjectTemplate(v8_global_template->PrototypeTemplate()); script_context_ = Context::New(NULL, v8_global_template->InstanceTemplate()); Context::Scope scope(script_context_); // Give the global object a prototype that allows V8 to access global // variables in another JavaScript environemnt over NPAPI. Local<Object> v8_global_prototype = Local<Object>::Cast(script_context_->Global()->GetPrototype()); Local<Object> v8_global_prototype2 = Local<Object>::Cast(v8_global_prototype->GetPrototype()); global_prototype_ = Persistent<Object>::New(v8_global_prototype2); NPToV8Object(v8_global_prototype2, global_np_object); function_map_ = Persistent<Object>::New(Object::New()); // Create a browser JavaScript function that can later be called to get the // type of an object (as the browser sees it). This is useful for determining // whether an object received over NPAPI is a function (which means its // proxy must be created from a FunctionTemplate rather than an // ObjectTemplate). static const char kIsFunctionScript[] = "(function(obj) { return obj instanceof Function; })"; np_is_function_function_ = NPEvaluateObject(kIsFunctionScript); // Create a browser JavaScript function that can later be used to enumerate // the properties of an object. This is used as a fallback if NPN_Evaluate // is not implemented by the browser (like Firefox 2) and the enumerate // callback is not implemented by the NPObject. static const char kEnumerateScript[] = "(function(object) {" " var properties = [];" " for (var property in object) {" " if (object.hasOwnProperty(property)) {" " properties[properties.length++] = property;" " }" " }" " return properties;" "})"; np_enumerate_function_ = NPEvaluateObject(kEnumerateScript); // Create a browser JavaScript function that can later be used to create // a wrapper around an V8 function proxy, making it appear to be a real // browser function. np_wrap_function_function_ = NPEvaluateObject( MakeWrapFunctionScript().c_str()); // Create an NPObject proxy for a V8 array. This is for the browser to use as // a prototype for creating new V8 arrays with slice(). np_empty_array_ = V8ToNPObject(v8::Array::New(0)); } void NPV8Bridge::ReleaseNPObjects() { np_v8_object_map_.clear(); np_construct_functions_.clear(); global_np_object_.Clear(); np_is_function_function_.Clear(); np_enumerate_function_.Clear(); np_wrap_function_function_.Clear(); np_empty_array_.Clear(); } v8::Handle<Context> NPV8Bridge::script_context() { return script_context_; } bool NPV8Bridge::Evaluate(const NPVariant* np_args, int numArgs, NPVariant* np_result) { HandleScope handle_scope; Context::Scope scope(script_context_); Local<Value> v8_code; if (numArgs == 1) { v8_code = NPToV8Variant(np_args[0]); } else { return false; } if (v8_code.IsEmpty() || !v8_code->IsString()) return false; TryCatch tryCatch; Local<Script> v8_script = v8::Script::Compile(v8_code->ToString()); if (tryCatch.HasCaught()) { ReportV8Exception(tryCatch); return false; } if (v8_script.IsEmpty()) return false; Local<Value> v8_result = v8_script->Run(); if (tryCatch.HasCaught()) { ReportV8Exception(tryCatch); return false; } if (v8_result.IsEmpty()) return false; *np_result = V8ToNPVariant(v8_result); return true; } void NPV8Bridge::SetGlobalProperty(const String& name, NPObjectPtr<NPObject>& np_object) { HandleScope handle_scope; Context::Scope scope(script_context_); script_context_->Global()->Set(v8::String::New(name.c_str()), NPToV8Object(np_object)); } NPVariant NPV8Bridge::V8ToNPVariant(Local<Value> value) { NPVariant np_variant; if (value.IsEmpty() || value->IsUndefined()) { VOID_TO_NPVARIANT(np_variant); } else if (value->IsNull()) { NULL_TO_NPVARIANT(np_variant); } else if (value->IsBoolean()) { BOOLEAN_TO_NPVARIANT(value->BooleanValue(), np_variant); } else if (value->IsInt32()) { INT32_TO_NPVARIANT(value->Int32Value(), np_variant); } else if (value->IsNumber()) { DOUBLE_TO_NPVARIANT(value->NumberValue(), np_variant); } else if (value->IsString()) { Local<v8::String> v8_string = value->ToString(); int utf8_length = v8_string->Length(); NPUTF8* utf8_chars = static_cast<NPUTF8*>(NPN_MemAlloc(utf8_length + 1)); v8_string->WriteUtf8(utf8_chars); STRINGN_TO_NPVARIANT(utf8_chars, utf8_length, np_variant); } else if (value->IsObject()) { Local<Object> v8_object = value->ToObject(); NPObjectPtr<NPObject> np_object = V8ToNPObject(v8_object); OBJECT_TO_NPVARIANT(np_object.Disown(), np_variant); } return np_variant; } Local<Value> NPV8Bridge::NPToV8Variant(const NPVariant& np_variant) { Local<Value> v8_result; switch (np_variant.type) { case NPVariantType_Void: v8_result = Local<Value>::New(Undefined()); break; case NPVariantType_Null: v8_result = Local<Value>::New(Null()); break; case NPVariantType_Bool: v8_result = Local<Value>::New( v8::Boolean::New(NPVARIANT_TO_BOOLEAN(np_variant))); break; case NPVariantType_Int32: v8_result = Local<Value>::New( Int32::New(NPVARIANT_TO_INT32(np_variant))); break; case NPVariantType_Double: v8_result = Local<Value>::New( Number::New(NPVARIANT_TO_DOUBLE(np_variant))); break; case NPVariantType_String: { NPString np_string = NPVARIANT_TO_STRING(np_variant); v8_result = Local<Value>::New( v8::String::New(np_string.utf8characters, np_string.utf8length)); break; } case NPVariantType_Object: v8_result = NPToV8Object( NPObjectPtr<NPObject>(NPVARIANT_TO_OBJECT(np_variant))); break; default: v8_result = Local<Value>(); break; } return v8_result; } NPObjectPtr<NPObject> NPV8Bridge::V8ToNPObject(Local<Value> v8_value) { NPObjectPtr<NPObject> np_object; if (!v8_value.IsEmpty() && v8_value->IsObject()) { Local<Object> v8_object = Local<Object>::Cast(v8_value); if (v8_object->InternalFieldCount() == 0) { // It is must be a V8 created JavaScript object (or function), a V8 // function proxy for an NP function or a V8 function proxy for a named // native method. If it is already associated with an NP object then that // will be stored in the "internal property". Return that if it's there, // otherwise create a new NP proxy. Local<v8::String> internal_name = v8::String::NewSymbol( kInternalProperty); Local<Value> v8_internal = v8_object->GetHiddenValue(internal_name); if (v8_internal.IsEmpty() || v8_internal->IsUndefined()) { // No existing NP object so create a proxy and store it in the "internal // property". np_object = NPV8Object::Create(this, v8_object); v8_internal = External::New(np_object.Get()); v8_object->SetHiddenValue(internal_name, v8_internal); } else { np_object = NPObjectPtr<NPObject>( static_cast<NPObject*>( Local<External>::Cast(v8_internal)->Value())); } // If it is a V8 function then wrap it in a browser function so that its // typeof will be reported as 'function' in the browser and it can be // used in cases where a real function is required (rather than an // object that just happens to be invocable. if (v8_value->IsFunction() && np_object->_class == &NPV8Object::np_class_) { np_object = WrapV8Function(np_object); } } else { // This is a V8 object proxy. The NP object is referenced from an internal // field. Local<Value> internal = v8_object->GetInternalField( V8_NP_OBJECT_WRAPPED); np_object = NPObjectPtr<NPObject>( static_cast<NPObject*>(Local<External>::Cast(internal)->Value())); } } return np_object; } // Wrap NPV8Object proxying a V8 function in a browser function so that its // typeof will be reported as 'function' in the browser and it can be // used in cases where a real function is required (rather than an // object that just happens to be invocable. // A new wrapper function is created whenever a V8 function crosses into the // browser. So === won't do the right thing in the browser. NPObjectPtr<NPObject> NPV8Bridge::WrapV8Function( const NPObjectPtr<NPObject>& np_object) { NPObjectPtr<NPObject> np_result = np_object; NPVariant np_args[2]; OBJECT_TO_NPVARIANT(np_object.Get(), np_args[0]); OBJECT_TO_NPVARIANT(np_empty_array_.Get(), np_args[1]); NPVariant np_variant; if (NPN_InvokeDefault(npp_, np_wrap_function_function_.Get(), np_args, 2, &np_variant)) { if (NPVARIANT_IS_OBJECT(np_variant)) { NPObjectPtr<NPObject> np_wrapper(NPVARIANT_TO_OBJECT(np_variant)); // Add a reference back to the NPV8Object so we can find it again. if (NPN_SetProperty(npp_, np_wrapper.Get(), np_proxy_identifier_, &np_args[0])) { np_result = np_wrapper; } } NPN_ReleaseVariantValue(&np_variant); } return np_result; } Local<Value> NPV8Bridge::NPToV8Object(const NPObjectPtr<NPObject>& np_object) { if (np_object.IsNull()) return Local<Value>::New(Null()); // This might be a wrapper for a function. Find the actual proxy in that // case. NPObjectPtr<NPObject> np_real_object = np_object; { // NPN_GetProperty might cause an O3D NPObject to set an error if the // property does not exist. Prevent that. It would be better to simply // test whether the property exists by calling NPN_HasProperty but that // is not supported in Mac Safari. ErrorSuppressor error_suppressor(service_locator_); NPVariant np_variant; if (NPN_GetProperty(npp_, np_real_object.Get(), np_proxy_identifier_, &np_variant)) { if (NPVARIANT_IS_OBJECT(np_variant)) { np_real_object = NPVARIANT_TO_OBJECT(np_variant); } NPN_ReleaseVariantValue(&np_variant); } } if (np_real_object->_class == &NPV8Object::np_class_) { NPV8Object* np_v8_object = static_cast<NPV8Object*>(np_real_object.Get()); return Local<Object>::New(np_v8_object->v8_object()); } else { NPV8ObjectMap::const_iterator it = np_v8_object_map_.find(np_real_object); if (it != np_v8_object_map_.end()) return Local<Object>::New(it->second); if (IsNPFunction(np_real_object)) { return NPToV8Function(np_real_object); } else { Local<Function> v8_function = v8_np_constructor_template_->GetFunction(); Local<Object> v8_object = v8_function->NewInstance(); if (!v8_object.IsEmpty()) { // NewInstance sets a JavaScript exception if it fails. Eventually // it'll be caught when control flow hits a TryCatch. Just make sure // not to dereference it before then. NPToV8Object(v8_object, np_real_object); } return v8_object; } } } void NPV8Bridge::NPToV8Object(v8::Local<Object> v8_target, const NPObjectPtr<NPObject>& np_object) { v8_target->SetInternalField(V8_NP_OBJECT_BRIDGE, External::New(this)); v8_target->SetInternalField(V8_NP_OBJECT_WRAPPED, External::New(np_object.Get())); RegisterV8Object(v8_target, np_object); } bool NPV8Bridge::IsNPFunction(const NPObjectPtr<NPObject>& np_object) { // Before invoking the potentially expensive instanceof function (it has to // go through the browser) check whether the object has a call // property. If it doesn't have one then it isn't a JavaScript // function. if (!NPN_HasProperty(npp_, np_object.Get(), np_call_identifier_)) { return false; } // If it looks like it might be a function then call the instanceof function // in the browser to confirm. bool is_function = false; NPVariant np_object_variant; OBJECT_TO_NPVARIANT(np_object.Get(), np_object_variant); NPVariant np_is_function; if (NPN_InvokeDefault(npp_, np_is_function_function_.Get(), &np_object_variant, 1, &np_is_function)) { if (NPVARIANT_IS_BOOLEAN(np_is_function)) { is_function = NPVARIANT_TO_BOOLEAN(np_is_function); } NPN_ReleaseVariantValue(&np_is_function); } return is_function; } v8::Local<v8::Function> NPV8Bridge::NPToV8Function( const NPObjectPtr<NPObject>& np_function) { Local<FunctionTemplate> v8_function_template = FunctionTemplate::New( V8CallFunction, External::New(this)); Local<Function> v8_function = v8_function_template->GetFunction(); Local<v8::String> internal_name = v8::String::NewSymbol( kInternalProperty); v8_function->SetHiddenValue(internal_name, External::New(np_function.Get())); // Copy function name from NP function. NPVariant np_name; if (NPN_GetProperty(npp_, np_function.Get(), np_name_identifier_, &np_name)) { Local<Value> v8_name_value = NPToV8Variant(np_name); NPN_ReleaseVariantValue(&np_name); if (!v8_name_value.IsEmpty() && v8_name_value->IsString()) { Local<v8::String> v8_name = Local<v8::String>::Cast(v8_name_value); v8_function->SetName(v8_name); } } RegisterV8Object(v8_function, np_function); return v8_function; } void NPV8Bridge::RegisterV8Object(v8::Local<v8::Object> v8_object, const NPObjectPtr<NPObject>& np_object) { np_v8_object_map_[np_object] = Persistent<Object>::New(v8_object); np_v8_object_map_[np_object].MakeWeak(this, NPV8WeakReferenceCallback); } bool NPV8Bridge::IsNPObjectReferenced(NPObjectPtr<NPObject> np_object) { return np_v8_object_map_.find(np_object) != np_v8_object_map_.end(); } void NPV8Bridge::InitializeV8ObjectTemplate( Local<ObjectTemplate> v8_object_template) { v8_object_template->SetInternalFieldCount( V8_NP_OBJECT_NUM_INTERNAL_FIELDS); v8_object_template->SetNamedPropertyHandler(V8NamedPropertyGetter, V8NamedPropertySetter, V8NamedPropertyQuery, V8NamedPropertyDeleter, V8NamedPropertyEnumerator); v8_object_template->SetIndexedPropertyHandler(V8IndexedPropertyGetter, V8IndexedPropertySetter, V8IndexedPropertyQuery, V8IndexedPropertyDeleter, V8IndexedPropertyEnumerator); v8_object_template->SetCallAsFunctionHandler(V8CallAsFunction); } void NPV8Bridge::NPV8WeakReferenceCallback(Persistent<Value> v8_value, void* parameter) { HandleScope handle_scope; NPV8Bridge* bridge = static_cast<NPV8Bridge*>(parameter); NPObjectPtr<NPObject> np_object = bridge->V8ToNPObject( Local<Value>::New(v8_value)); bridge->np_v8_object_map_.erase(np_object); } void NPV8Bridge::ReportV8Exception(const TryCatch& v8_try_catch) { if (v8_try_catch.HasCaught()) { Local<Message> v8_message = v8_try_catch.Message(); if (v8_message.IsEmpty()) { Local<Value> v8_exception = v8_try_catch.Exception(); if (v8_exception.IsEmpty()) { error_status_->SetLastError( "An unknown exception ocurred while executing V8 JavaScript code"); } else { v8::String::Utf8Value as_utf8(v8_exception); if (*as_utf8) { error_status_->SetLastError(*as_utf8); } else { error_status_->SetLastError( "An exception was thrown but its toString method failed"); } } } else { String source_line(*v8::String::Utf8Value(v8_message->GetSourceLine())); String text(*v8::String::Utf8Value(v8_message->Get())); String message = text + " in " + source_line; error_status_->SetLastError(message); } } } v8::Local<v8::Array> NPV8Bridge::NPToV8IdentifierArray( const NPVariant& np_array, bool named) { Local<Array> v8_array; if (!NPVARIANT_IS_OBJECT(np_array)) return v8_array; NPObject* np_array_object = NPVARIANT_TO_OBJECT(np_array); NPVariant np_length; if (NPN_GetProperty(npp_, np_array_object, np_length_identifier_, &np_length)) { Local<Value> v8_length = NPToV8Variant(np_length); NPN_ReleaseVariantValue(&np_length); if (v8_length.IsEmpty() || !v8_length->IsNumber()) return v8_array; int length = v8_length->Int32Value(); Local<Array> v8_untrimmed_array = Array::New(length); int num_elements = 0; for (int i = 0; i < length; ++i) { NPVariant np_element; if (!NPN_GetProperty(npp_, np_array_object, NPN_GetIntIdentifier(i), &np_element)) return Local<Array>(); Local<Value> v8_element = NPToV8Variant(np_element); NPN_ReleaseVariantValue(&np_element); if (v8_element->IsString() == named) { v8_untrimmed_array->Set(Int32::New(num_elements), v8_element); ++num_elements; } } v8_array = Array::New(num_elements); for (int i = 0; i < num_elements; ++i) { Local<Integer> i_handle = Integer::New(i); v8_array->Set(i_handle, v8_untrimmed_array->Get(i_handle)); } } return v8_array; } Local<Array> NPV8Bridge::NPToV8IdentifierArray(const NPIdentifier* ids, uint32_t id_count, bool named) { int num_elements = 0; for (uint32_t i = 0; i < id_count; ++i) { if (NPN_IdentifierIsString(ids[i]) == named) { ++num_elements; } } Local<Array> v8_array = Array::New(num_elements); int j = 0; for (uint32_t i = 0; i < id_count; ++i) { if (NPN_IdentifierIsString(ids[i]) == named) { v8_array->Set(Integer::New(j), NPToV8Identifier(ids[i])); ++j; } } return v8_array; } Local<Array> NPV8Bridge::Enumerate(const NPObjectPtr<NPObject> np_object, bool named) { Local<Array> v8_array; // First try calling NPN_Enumerate. This will return false if the browser // does not support NPN_Enumerate. NPIdentifier* ids; uint32_t id_count; if (NPN_Enumerate(npp_, np_object.Get(), &ids, &id_count)) { v8_array = NPToV8IdentifierArray(ids, id_count, named); NPN_MemFree(ids); } else if (np_object->_class->structVersion >= NP_CLASS_STRUCT_VERSION_ENUM && np_object->_class->enumerate != NULL && np_object->_class->enumerate(np_object.Get(), &ids, &id_count)) { // Next see if the object has an enumerate callback and invoke it // directly. This is the path used when V8 enumerates the // properties of a native object if the browser does not support // NPN_Enumerate. v8_array = NPToV8IdentifierArray(ids, id_count, named); NPN_MemFree(ids); } else { // The final fallback is to invoke a JavaScript function that // enumerates all the properties into an array and returns it to // the plugin. NPVariant np_result; NPVariant np_arg; OBJECT_TO_NPVARIANT(np_object.Get(), np_arg); if (NPN_InvokeDefault(npp_, np_enumerate_function_.Get(), &np_arg, 1, &np_result)) { v8_array = NPToV8IdentifierArray(np_result, named); NPN_ReleaseVariantValue(&np_result); } } return v8_array; } v8::Handle<Value> NPV8Bridge::V8PropertyGetter(Local<Value> v8_name, const AccessorInfo& info) { Local<Value> v8_result; Local<Object> holder = info.Holder(); NPV8Bridge* bridge = static_cast<NPV8Bridge*>( Local<External>::Cast( holder->GetInternalField(V8_NP_OBJECT_BRIDGE))->Value()); Context::Scope scope(bridge->script_context()); if (holder.IsEmpty()) return v8_result; NPObjectPtr<NPObject> np_object = bridge->V8ToNPObject(holder); if (np_object.IsNull()) return v8_result; NPIdentifier np_name = V8ToNPIdentifier(v8_name); if (np_name == NULL) return v8_result; NPVariant np_result; if (NPN_HasProperty(bridge->npp_, np_object.Get(), np_name) && NPN_GetProperty(bridge->npp_, np_object.Get(), np_name, &np_result)) { v8_result = bridge->NPToV8Variant(np_result); NPN_ReleaseVariantValue(&np_result); } else if (np_object->_class->hasMethod != NULL && np_object->_class->hasMethod(np_object.Get(), np_name)) { // It's not calling NPN_HasMethod here because of a bug in Firefox // (Mozilla bug ID 467945), where NPN_HasMethod forwards to the object's // hasProperty function instead. The workaround is to sidestep npruntime. v8_result = bridge->function_map_->Get(v8_name); if (v8_result.IsEmpty() || v8_result->IsUndefined()) { Local<FunctionTemplate> function_template = FunctionTemplate::New(V8CallNamedMethod, v8_name); v8_result = function_template->GetFunction(); bridge->function_map_->Set(v8_name, v8_result); } } return v8_result; } v8::Handle<Value> NPV8Bridge::V8PropertySetter( Local<Value> v8_name, Local<Value> v8_value, const AccessorInfo& info) { Local<Value> v8_result; Local<Object> holder = info.Holder(); NPV8Bridge* bridge = static_cast<NPV8Bridge*>( Local<External>::Cast( holder->GetInternalField(V8_NP_OBJECT_BRIDGE))->Value()); Context::Scope scope(bridge->script_context()); NPObjectPtr<NPObject> np_object = bridge->V8ToNPObject(holder); if (np_object.IsNull()) return v8_result; NPIdentifier np_name = V8ToNPIdentifier(v8_name); if (np_name == NULL) return v8_result; NPVariant np_value = bridge->V8ToNPVariant(v8_value); NPN_SetProperty(bridge->npp_, np_object.Get(), np_name, &np_value); NPN_ReleaseVariantValue(&np_value); return v8_result; } v8::Handle<v8::Boolean> NPV8Bridge::V8PropertyQuery(Local<Value> v8_name, const AccessorInfo& info) { Local<Object> holder = info.Holder(); NPV8Bridge* bridge = static_cast<NPV8Bridge*>( Local<External>::Cast( holder->GetInternalField(V8_NP_OBJECT_BRIDGE))->Value()); Context::Scope scope(bridge->script_context()); NPObjectPtr<NPObject> np_object = bridge->V8ToNPObject(holder); if (np_object.IsNull()) return v8::Handle<v8::Boolean>(); NPIdentifier np_name = V8ToNPIdentifier(v8_name); if (np_name == NULL) return v8::Handle<v8::Boolean>(); bool has = NPN_HasProperty(bridge->npp_, np_object.Get(), np_name) || NPN_HasMethod(bridge->npp_, np_object.Get(), np_name); return v8::Boolean::New(has); } v8::Handle<v8::Boolean> NPV8Bridge::V8PropertyDeleter( Local<Value> v8_name, const AccessorInfo& info) { Local<Object> holder = info.Holder(); NPV8Bridge* bridge = static_cast<NPV8Bridge*>( Local<External>::Cast( holder->GetInternalField(V8_NP_OBJECT_BRIDGE))->Value()); Context::Scope scope(bridge->script_context()); NPObjectPtr<NPObject> np_object = bridge->V8ToNPObject(holder); if (np_object.IsNull()) return v8::Handle<v8::Boolean>(); NPIdentifier np_name = V8ToNPIdentifier(v8_name); if (np_name == NULL) return v8::Handle<v8::Boolean>(); // Workaround for a bug in Chrome. Chrome does not check whether the // removeProperty callback is implemented before calling it, causing // NPN_RemoveProperty to crash if it is not. So do the check before calling // it. bool deleted = np_object->_class->removeProperty != NULL && NPN_RemoveProperty(bridge->npp_, np_object.Get(), np_name); return v8::Boolean::New(deleted); } v8::Handle<Value> NPV8Bridge::V8NamedPropertyGetter(Local<v8::String> v8_name, const AccessorInfo& info) { return V8PropertyGetter(v8_name, info); } v8::Handle<Value> NPV8Bridge::V8NamedPropertySetter(Local<v8::String> v8_name, Local<Value> v8_value, const AccessorInfo& info) { return V8PropertySetter(v8_name, v8_value, info); } v8::Handle<v8::Boolean> NPV8Bridge::V8NamedPropertyQuery( Local<v8::String> v8_name, const AccessorInfo& info) { return V8PropertyQuery(v8_name, info); } v8::Handle<v8::Boolean> NPV8Bridge::V8NamedPropertyDeleter( Local<v8::String> v8_name, const AccessorInfo& info) { return V8PropertyDeleter(v8_name, info); } v8::Handle<Array> NPV8Bridge::V8NamedPropertyEnumerator( const AccessorInfo& info) { Local<Object> holder = info.Holder(); NPV8Bridge* bridge = static_cast<NPV8Bridge*>( Local<External>::Cast( holder->GetInternalField(V8_NP_OBJECT_BRIDGE))->Value()); Context::Scope scope(bridge->script_context()); NPObjectPtr<NPObject> np_object = bridge->V8ToNPObject(holder); if (np_object.IsNull()) return v8::Handle<Array>(); return bridge->Enumerate(np_object, true); } v8::Handle<Value> NPV8Bridge::V8IndexedPropertyGetter( uint32_t index, const AccessorInfo& info) { return V8PropertyGetter(Integer::New(index), info); } v8::Handle<Value> NPV8Bridge::V8IndexedPropertySetter( uint32_t index, Local<Value> v8_value, const AccessorInfo& info) { return V8PropertySetter(Integer::New(index), v8_value, info); } v8::Handle<v8::Boolean> NPV8Bridge::V8IndexedPropertyQuery( uint32_t index, const AccessorInfo& info) { return V8PropertyQuery(Integer::New(index), info); } v8::Handle<v8::Boolean> NPV8Bridge::V8IndexedPropertyDeleter( uint32_t index, const AccessorInfo& info) { return V8PropertyDeleter(Integer::New(index), info); } v8::Handle<Array> NPV8Bridge::V8IndexedPropertyEnumerator( const AccessorInfo& info) { Local<Object> holder = info.Holder(); NPV8Bridge* bridge = static_cast<NPV8Bridge*>( Local<External>::Cast( holder->GetInternalField(V8_NP_OBJECT_BRIDGE))->Value()); Context::Scope scope(bridge->script_context()); NPObjectPtr<NPObject> np_object = bridge->V8ToNPObject(holder); if (np_object.IsNull()) return v8::Handle<Array>(); return bridge->Enumerate(np_object, false); } v8::Handle<Value> NPV8Bridge::V8CallNamedMethod(const Arguments& args) { Local<Value> v8_result; if (args.IsConstructCall()) return v8_result; Local<Object> v8_holder = args.Holder(); NPV8Bridge* bridge = static_cast<NPV8Bridge*>( Local<External>::Cast( v8_holder->GetInternalField(V8_NP_OBJECT_BRIDGE))->Value()); Context::Scope scope(bridge->script_context()); NPObjectPtr<NPObject> np_this = bridge->V8ToNPObject(v8_holder); if (np_this.IsNull()) return v8_result; v8::Handle<Value> v8_name = args.Data(); NPIdentifier np_name = V8ToNPIdentifier(v8_name); if (np_name == NULL) return v8_result; std::vector<NPVariant> np_args(args.Length()); for (int i = 0; i != args.Length(); ++i) { np_args[i] = bridge->V8ToNPVariant(args[i]); } NPVariant np_result; if (NPN_Invoke(bridge->npp_, np_this.Get(), np_name, args.Length() == 0 ? NULL : &np_args.front(), args.Length(), &np_result)) { v8_result = bridge->NPToV8Variant(np_result); NPN_ReleaseVariantValue(&np_result); } for (int i = 0; i != args.Length(); ++i) { NPN_ReleaseVariantValue(&np_args[i]); } return v8_result; } v8::Handle<Value> NPV8Bridge::V8CallFunction(const Arguments& args) { Local<Value> v8_result; NPV8Bridge* bridge = static_cast<NPV8Bridge*>( Local<External>::Cast(args.Data())->Value()); Context::Scope scope(bridge->script_context()); Local<Function> v8_callee = args.Callee(); Local<Object> v8_this = args.This(); // Allocate an extra argument element for the "this" pointer. This is only // used if we end up invoking a method through function.call(this, arg0, ..., // argn). std::vector<NPVariant> np_args(args.Length() + 1); VOID_TO_NPVARIANT(np_args[0]); for (int i = 0; i != args.Length(); ++i) { np_args[i + 1] = bridge->V8ToNPVariant(args[i]); } // Need to determine whether the object was called as a standalone function, // a method or a constructor. The constructor case is easy: args has a flag // for it. If the function was called standalone then "this" will reference // the global object. Otherwise assume it is a method invocation. NPVariant np_result; if (args.IsConstructCall()) { // NPN_Construct was giving me trouble on some browsers (like Chrome). It // might have better support in the future. For the time being, I'm using // this alternative. NPObjectPtr<NPObject> np_construct_function = bridge->GetNPConstructFunction(args.Length()); np_args[0] = bridge->V8ToNPVariant(v8_callee); if (NPN_InvokeDefault(bridge->npp_, np_construct_function.Get(), &np_args[0], args.Length() + 1, &np_result)) { v8_result = bridge->NPToV8Variant(np_result); NPN_ReleaseVariantValue(&np_result); } } else if (v8_this == bridge->script_context_->Global()) { // Treat standalone case specially. We use NPN_InvokeDefault rather than // NPN_Invoke with the "call" method because we want to have "this" refer // to the browser's global environment rather than the V8 global // environment. NPObjectPtr<NPObject> np_callee = bridge->V8ToNPObject(v8_callee); if (NPN_InvokeDefault(bridge->npp_, np_callee.Get(), 1 + &np_args[0], args.Length(), &np_result)) { v8_result = bridge->NPToV8Variant(np_result); NPN_ReleaseVariantValue(&np_result); } } else { // Invoke a function as a method by invoking its "call" call method. This // is not the usual way of invoking a method in runtime. The usual way would // to be to call NPN_Invoke on the target object (the one to be bound to // "this") with a method name. But we don't know the method name. We don't // even know if the function is assigned to one of the properties of the // target object. To avoid that trouble, we invoke the function's "call" // method with "this" as an explicit argument. NPObjectPtr<NPObject> np_callee = bridge->V8ToNPObject(v8_callee); np_args[0] = bridge->V8ToNPVariant(v8_this); if (NPN_Invoke(bridge->npp_, np_callee.Get(), bridge->np_call_identifier_, &np_args[0], args.Length() + 1, &np_result)) { v8_result = bridge->NPToV8Variant(np_result); NPN_ReleaseVariantValue(&np_result); } } for (int i = 0; i != args.Length() + 1; ++i) { NPN_ReleaseVariantValue(&np_args[i]); } return v8_result; } v8::Handle<Value> NPV8Bridge::V8CallAsFunction(const Arguments& args) { Local<Value> v8_result; Local<Object> v8_callee = args.This(); NPV8Bridge* bridge = static_cast<NPV8Bridge*>( Local<External>::Cast( v8_callee->GetInternalField(V8_NP_OBJECT_BRIDGE))->Value()); Context::Scope scope(bridge->script_context()); std::vector<NPVariant> np_args(args.Length()); for (int i = 0; i != args.Length(); ++i) { np_args[i] = bridge->V8ToNPVariant(args[i]); } NPVariant np_result; NPObjectPtr<NPObject> np_callee = bridge->V8ToNPObject(v8_callee); if (NPN_InvokeDefault(bridge->npp_, np_callee.Get(), args.Length() == 0 ? NULL : &np_args[0], args.Length(), &np_result)) { v8_result = bridge->NPToV8Variant(np_result); NPN_ReleaseVariantValue(&np_result); } for (int i = 0; i != args.Length(); ++i) { NPN_ReleaseVariantValue(&np_args[i]); } return v8_result; } // Evaluates and returns an NP function that will construct an object. The // function takes the constructor and constructor arguments as arguments. // I'm doing this because not all browsers seem to support calling NPN_Construct // on JavaScript constructor functions. NPObjectPtr<NPObject> NPV8Bridge::GetNPConstructFunction(int arity) { NPConstructFunctionMap::const_iterator it = np_construct_functions_.find( arity); if (it != np_construct_functions_.end()) return it->second; // Build a function that looks like: // (function (c,p0,p1) { return new c(p0,p1); }) std::ostringstream code; code << "(function(c"; for (int i = 0; i != arity; ++i) { code << ",p" << i; } code << ") { return new c("; String separator = ""; for (int i = 0; i != arity; ++i) { code << separator << String("p") << i; separator = ","; } code << "); })"; return NPEvaluateObject(code.str().c_str()); } } // namespace o3d