| // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "content/renderer/pepper/plugin_object.h" |
| |
| #include "base/logging.h" |
| #include "base/memory/ref_counted.h" |
| #include "base/memory/scoped_ptr.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/strings/string_util.h" |
| #include "content/renderer/pepper/npapi_glue.h" |
| #include "content/renderer/pepper/pepper_plugin_instance_impl.h" |
| #include "content/renderer/pepper/plugin_module.h" |
| #include "ppapi/c/dev/ppb_var_deprecated.h" |
| #include "ppapi/c/dev/ppp_class_deprecated.h" |
| #include "ppapi/c/pp_resource.h" |
| #include "ppapi/c/pp_var.h" |
| #include "ppapi/shared_impl/ppapi_globals.h" |
| #include "ppapi/shared_impl/resource_tracker.h" |
| #include "ppapi/shared_impl/var.h" |
| #include "ppapi/shared_impl/var_tracker.h" |
| #include "third_party/WebKit/public/web/WebBindings.h" |
| #include "third_party/npapi/bindings/npapi.h" |
| #include "third_party/npapi/bindings/npruntime.h" |
| |
| using ppapi::PpapiGlobals; |
| using ppapi::StringVar; |
| using ppapi::Var; |
| using WebKit::WebBindings; |
| |
| namespace content { |
| |
| namespace { |
| |
| const char kInvalidValueException[] = "Error: Invalid value"; |
| |
| // NPObject implementation in terms of PPP_Class_Deprecated -------------------- |
| |
| NPObject* WrapperClass_Allocate(NPP npp, NPClass* unused) { |
| return PluginObject::AllocateObjectWrapper(); |
| } |
| |
| void WrapperClass_Deallocate(NPObject* np_object) { |
| PluginObject* plugin_object = PluginObject::FromNPObject(np_object); |
| if (!plugin_object) |
| return; |
| plugin_object->ppp_class()->Deallocate(plugin_object->ppp_class_data()); |
| delete plugin_object; |
| } |
| |
| void WrapperClass_Invalidate(NPObject* object) { |
| } |
| |
| bool WrapperClass_HasMethod(NPObject* object, NPIdentifier method_name) { |
| NPObjectAccessorWithIdentifier accessor(object, method_name, false); |
| if (!accessor.is_valid()) |
| return false; |
| |
| PPResultAndExceptionToNPResult result_converter( |
| accessor.object()->GetNPObject(), NULL); |
| bool rv = accessor.object()->ppp_class()->HasMethod( |
| accessor.object()->ppp_class_data(), accessor.identifier(), |
| result_converter.exception()); |
| result_converter.CheckExceptionForNoResult(); |
| return rv; |
| } |
| |
| bool WrapperClass_Invoke(NPObject* object, NPIdentifier method_name, |
| const NPVariant* argv, uint32_t argc, |
| NPVariant* result) { |
| NPObjectAccessorWithIdentifier accessor(object, method_name, false); |
| if (!accessor.is_valid()) |
| return false; |
| |
| PPResultAndExceptionToNPResult result_converter( |
| accessor.object()->GetNPObject(), result); |
| PPVarArrayFromNPVariantArray args(accessor.object()->instance(), |
| argc, argv); |
| |
| // For the OOP plugin case we need to grab a reference on the plugin module |
| // object to ensure that it is not destroyed courtsey an incoming |
| // ExecuteScript call which destroys the plugin module and in turn the |
| // dispatcher. |
| scoped_refptr<PluginModule> ref(accessor.object()->instance()->module()); |
| |
| return result_converter.SetResult(accessor.object()->ppp_class()->Call( |
| accessor.object()->ppp_class_data(), accessor.identifier(), |
| argc, args.array(), result_converter.exception())); |
| } |
| |
| bool WrapperClass_InvokeDefault(NPObject* np_object, const NPVariant* argv, |
| uint32_t argc, NPVariant* result) { |
| PluginObject* obj = PluginObject::FromNPObject(np_object); |
| if (!obj) |
| return false; |
| |
| PPVarArrayFromNPVariantArray args(obj->instance(), argc, argv); |
| PPResultAndExceptionToNPResult result_converter(obj->GetNPObject(), result); |
| |
| // For the OOP plugin case we need to grab a reference on the plugin module |
| // object to ensure that it is not destroyed courtsey an incoming |
| // ExecuteScript call which destroys the plugin module and in turn the |
| // dispatcher. |
| scoped_refptr<PluginModule> ref(obj->instance()->module()); |
| |
| result_converter.SetResult(obj->ppp_class()->Call( |
| obj->ppp_class_data(), PP_MakeUndefined(), argc, args.array(), |
| result_converter.exception())); |
| return result_converter.success(); |
| } |
| |
| bool WrapperClass_HasProperty(NPObject* object, NPIdentifier property_name) { |
| NPObjectAccessorWithIdentifier accessor(object, property_name, true); |
| if (!accessor.is_valid()) |
| return false; |
| |
| PPResultAndExceptionToNPResult result_converter( |
| accessor.object()->GetNPObject(), NULL); |
| bool rv = accessor.object()->ppp_class()->HasProperty( |
| accessor.object()->ppp_class_data(), accessor.identifier(), |
| result_converter.exception()); |
| result_converter.CheckExceptionForNoResult(); |
| return rv; |
| } |
| |
| bool WrapperClass_GetProperty(NPObject* object, NPIdentifier property_name, |
| NPVariant* result) { |
| NPObjectAccessorWithIdentifier accessor(object, property_name, true); |
| if (!accessor.is_valid()) |
| return false; |
| |
| PPResultAndExceptionToNPResult result_converter( |
| accessor.object()->GetNPObject(), result); |
| return result_converter.SetResult(accessor.object()->ppp_class()->GetProperty( |
| accessor.object()->ppp_class_data(), accessor.identifier(), |
| result_converter.exception())); |
| } |
| |
| bool WrapperClass_SetProperty(NPObject* object, NPIdentifier property_name, |
| const NPVariant* value) { |
| NPObjectAccessorWithIdentifier accessor(object, property_name, true); |
| if (!accessor.is_valid()) |
| return false; |
| |
| PPResultAndExceptionToNPResult result_converter( |
| accessor.object()->GetNPObject(), NULL); |
| PP_Var value_var = NPVariantToPPVar(accessor.object()->instance(), value); |
| accessor.object()->ppp_class()->SetProperty( |
| accessor.object()->ppp_class_data(), accessor.identifier(), value_var, |
| result_converter.exception()); |
| PpapiGlobals::Get()->GetVarTracker()->ReleaseVar(value_var); |
| return result_converter.CheckExceptionForNoResult(); |
| } |
| |
| bool WrapperClass_RemoveProperty(NPObject* object, NPIdentifier property_name) { |
| NPObjectAccessorWithIdentifier accessor(object, property_name, true); |
| if (!accessor.is_valid()) |
| return false; |
| |
| PPResultAndExceptionToNPResult result_converter( |
| accessor.object()->GetNPObject(), NULL); |
| accessor.object()->ppp_class()->RemoveProperty( |
| accessor.object()->ppp_class_data(), accessor.identifier(), |
| result_converter.exception()); |
| return result_converter.CheckExceptionForNoResult(); |
| } |
| |
| bool WrapperClass_Enumerate(NPObject* object, NPIdentifier** values, |
| uint32_t* count) { |
| *values = NULL; |
| *count = 0; |
| PluginObject* obj = PluginObject::FromNPObject(object); |
| if (!obj) |
| return false; |
| |
| uint32_t property_count = 0; |
| PP_Var* properties = NULL; // Must be freed! |
| PPResultAndExceptionToNPResult result_converter(obj->GetNPObject(), NULL); |
| obj->ppp_class()->GetAllPropertyNames(obj->ppp_class_data(), |
| &property_count, &properties, |
| result_converter.exception()); |
| |
| // Convert the array of PP_Var to an array of NPIdentifiers. If any |
| // conversions fail, we will set the exception. |
| if (!result_converter.has_exception()) { |
| if (property_count > 0) { |
| *values = static_cast<NPIdentifier*>( |
| malloc(sizeof(NPIdentifier) * property_count)); |
| *count = 0; // Will be the number of items successfully converted. |
| for (uint32_t i = 0; i < property_count; ++i) { |
| if (!((*values)[i] = PPVarToNPIdentifier(properties[i]))) { |
| // Throw an exception for the failed convertion. |
| *result_converter.exception() = |
| StringVar::StringToPPVar(kInvalidValueException); |
| break; |
| } |
| (*count)++; |
| } |
| |
| if (result_converter.has_exception()) { |
| // We don't actually have to free the identifiers we converted since |
| // all identifiers leak anyway :( . |
| free(*values); |
| *values = NULL; |
| *count = 0; |
| } |
| } |
| } |
| |
| // This will actually throw the exception, either from GetAllPropertyNames, |
| // or if anything was set during the conversion process. |
| result_converter.CheckExceptionForNoResult(); |
| |
| // Release the PP_Var that the plugin allocated. On success, they will all |
| // be converted to NPVariants, and on failure, we want them to just go away. |
| ppapi::VarTracker* var_tracker = PpapiGlobals::Get()->GetVarTracker(); |
| for (uint32_t i = 0; i < property_count; ++i) |
| var_tracker->ReleaseVar(properties[i]); |
| free(properties); |
| return result_converter.success(); |
| } |
| |
| bool WrapperClass_Construct(NPObject* object, const NPVariant* argv, |
| uint32_t argc, NPVariant* result) { |
| PluginObject* obj = PluginObject::FromNPObject(object); |
| if (!obj) |
| return false; |
| |
| PPVarArrayFromNPVariantArray args(obj->instance(), argc, argv); |
| PPResultAndExceptionToNPResult result_converter(obj->GetNPObject(), result); |
| return result_converter.SetResult(obj->ppp_class()->Construct( |
| obj->ppp_class_data(), argc, args.array(), |
| result_converter.exception())); |
| } |
| |
| const NPClass wrapper_class = { |
| NP_CLASS_STRUCT_VERSION, |
| WrapperClass_Allocate, |
| WrapperClass_Deallocate, |
| WrapperClass_Invalidate, |
| WrapperClass_HasMethod, |
| WrapperClass_Invoke, |
| WrapperClass_InvokeDefault, |
| WrapperClass_HasProperty, |
| WrapperClass_GetProperty, |
| WrapperClass_SetProperty, |
| WrapperClass_RemoveProperty, |
| WrapperClass_Enumerate, |
| WrapperClass_Construct |
| }; |
| |
| } // namespace |
| |
| // PluginObject ---------------------------------------------------------------- |
| |
| struct PluginObject::NPObjectWrapper : public NPObject { |
| // Points to the var object that owns this wrapper. This value may be NULL |
| // if there is no var owning this wrapper. This can happen if the plugin |
| // releases all references to the var, but a reference to the underlying |
| // NPObject is still held by script on the page. |
| PluginObject* obj; |
| }; |
| |
| PluginObject::PluginObject(PepperPluginInstanceImpl* instance, |
| NPObjectWrapper* object_wrapper, |
| const PPP_Class_Deprecated* ppp_class, |
| void* ppp_class_data) |
| : instance_(instance), |
| object_wrapper_(object_wrapper), |
| ppp_class_(ppp_class), |
| ppp_class_data_(ppp_class_data) { |
| // Make the object wrapper refer back to this class so our NPObject |
| // implementation can call back into the Pepper layer. |
| object_wrapper_->obj = this; |
| instance_->AddPluginObject(this); |
| } |
| |
| PluginObject::~PluginObject() { |
| // The wrapper we made for this NPObject may still have a reference to it |
| // from JavaScript, so we clear out its ObjectVar back pointer which will |
| // cause all calls "up" to the plugin to become NOPs. Our ObjectVar base |
| // class will release our reference to the object, which may or may not |
| // delete the NPObject. |
| DCHECK(object_wrapper_->obj == this); |
| object_wrapper_->obj = NULL; |
| instance_->RemovePluginObject(this); |
| } |
| |
| PP_Var PluginObject::Create(PepperPluginInstanceImpl* instance, |
| const PPP_Class_Deprecated* ppp_class, |
| void* ppp_class_data) { |
| // This will internally end up calling our AllocateObjectWrapper via the |
| // WrapperClass_Allocated function which will have created an object wrapper |
| // appropriate for this class (derived from NPObject). |
| NPObjectWrapper* wrapper = static_cast<NPObjectWrapper*>( |
| WebBindings::createObject(instance->instanceNPP(), |
| const_cast<NPClass*>(&wrapper_class))); |
| |
| // This object will register itself both with the NPObject and with the |
| // PluginModule. The NPObject will normally handle its lifetime, and it |
| // will get deleted in the destroy method. It may also get deleted when the |
| // plugin module is deallocated. |
| new PluginObject(instance, wrapper, ppp_class, ppp_class_data); |
| |
| // We can just use a normal ObjectVar to refer to this object from the |
| // plugin. It will hold a ref to the underlying NPObject which will in turn |
| // hold our pluginObject. |
| PP_Var obj_var(NPObjectToPPVar(instance, wrapper)); |
| |
| // Note that the ObjectVar constructor incremented the reference count, and so |
| // did WebBindings::createObject above. Now that the PP_Var has taken |
| // ownership, we need to release to balance out the createObject reference |
| // count bump. |
| WebBindings::releaseObject(wrapper); |
| return obj_var; |
| } |
| |
| NPObject* PluginObject::GetNPObject() const { |
| return object_wrapper_; |
| } |
| |
| // static |
| bool PluginObject::IsInstanceOf(NPObject* np_object, |
| const PPP_Class_Deprecated* ppp_class, |
| void** ppp_class_data) { |
| // Validate that this object is implemented by our wrapper class before |
| // trying to get the PluginObject. |
| if (np_object->_class != &wrapper_class) |
| return false; |
| |
| PluginObject* plugin_object = FromNPObject(np_object); |
| if (!plugin_object) |
| return false; // Object is no longer alive. |
| |
| if (plugin_object->ppp_class() != ppp_class) |
| return false; |
| if (ppp_class_data) |
| *ppp_class_data = plugin_object->ppp_class_data(); |
| return true; |
| } |
| |
| // static |
| PluginObject* PluginObject::FromNPObject(NPObject* object) { |
| return static_cast<NPObjectWrapper*>(object)->obj; |
| } |
| |
| // static |
| NPObject* PluginObject::AllocateObjectWrapper() { |
| NPObjectWrapper* wrapper = new NPObjectWrapper; |
| memset(wrapper, 0, sizeof(NPObjectWrapper)); |
| return wrapper; |
| } |
| |
| } // namespace content |
| |