// 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 blink::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