Home | History | Annotate | Download | only in pepper
      1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "content/renderer/pepper/plugin_object.h"
      6 
      7 #include "base/logging.h"
      8 #include "base/memory/ref_counted.h"
      9 #include "base/memory/scoped_ptr.h"
     10 #include "base/strings/string_number_conversions.h"
     11 #include "base/strings/string_util.h"
     12 #include "content/renderer/pepper/npapi_glue.h"
     13 #include "content/renderer/pepper/pepper_plugin_instance_impl.h"
     14 #include "content/renderer/pepper/plugin_module.h"
     15 #include "ppapi/c/dev/ppb_var_deprecated.h"
     16 #include "ppapi/c/dev/ppp_class_deprecated.h"
     17 #include "ppapi/c/pp_resource.h"
     18 #include "ppapi/c/pp_var.h"
     19 #include "ppapi/shared_impl/ppapi_globals.h"
     20 #include "ppapi/shared_impl/resource_tracker.h"
     21 #include "ppapi/shared_impl/var.h"
     22 #include "ppapi/shared_impl/var_tracker.h"
     23 #include "third_party/WebKit/public/web/WebBindings.h"
     24 #include "third_party/npapi/bindings/npapi.h"
     25 #include "third_party/npapi/bindings/npruntime.h"
     26 
     27 using ppapi::PpapiGlobals;
     28 using ppapi::StringVar;
     29 using ppapi::Var;
     30 using WebKit::WebBindings;
     31 
     32 namespace content {
     33 
     34 namespace {
     35 
     36 const char kInvalidValueException[] = "Error: Invalid value";
     37 
     38 // NPObject implementation in terms of PPP_Class_Deprecated --------------------
     39 
     40 NPObject* WrapperClass_Allocate(NPP npp, NPClass* unused) {
     41   return PluginObject::AllocateObjectWrapper();
     42 }
     43 
     44 void WrapperClass_Deallocate(NPObject* np_object) {
     45   PluginObject* plugin_object = PluginObject::FromNPObject(np_object);
     46   if (!plugin_object)
     47     return;
     48   plugin_object->ppp_class()->Deallocate(plugin_object->ppp_class_data());
     49   delete plugin_object;
     50 }
     51 
     52 void WrapperClass_Invalidate(NPObject* object) {
     53 }
     54 
     55 bool WrapperClass_HasMethod(NPObject* object, NPIdentifier method_name) {
     56   NPObjectAccessorWithIdentifier accessor(object, method_name, false);
     57   if (!accessor.is_valid())
     58     return false;
     59 
     60   PPResultAndExceptionToNPResult result_converter(
     61       accessor.object()->GetNPObject(), NULL);
     62   bool rv = accessor.object()->ppp_class()->HasMethod(
     63       accessor.object()->ppp_class_data(), accessor.identifier(),
     64       result_converter.exception());
     65   result_converter.CheckExceptionForNoResult();
     66   return rv;
     67 }
     68 
     69 bool WrapperClass_Invoke(NPObject* object, NPIdentifier method_name,
     70                          const NPVariant* argv, uint32_t argc,
     71                          NPVariant* result) {
     72   NPObjectAccessorWithIdentifier accessor(object, method_name, false);
     73   if (!accessor.is_valid())
     74     return false;
     75 
     76   PPResultAndExceptionToNPResult result_converter(
     77       accessor.object()->GetNPObject(), result);
     78   PPVarArrayFromNPVariantArray args(accessor.object()->instance(),
     79                                     argc, argv);
     80 
     81   // For the OOP plugin case we need to grab a reference on the plugin module
     82   // object to ensure that it is not destroyed courtsey an incoming
     83   // ExecuteScript call which destroys the plugin module and in turn the
     84   // dispatcher.
     85   scoped_refptr<PluginModule> ref(accessor.object()->instance()->module());
     86 
     87   return result_converter.SetResult(accessor.object()->ppp_class()->Call(
     88       accessor.object()->ppp_class_data(), accessor.identifier(),
     89       argc, args.array(), result_converter.exception()));
     90 }
     91 
     92 bool WrapperClass_InvokeDefault(NPObject* np_object, const NPVariant* argv,
     93                                 uint32_t argc, NPVariant* result) {
     94   PluginObject* obj = PluginObject::FromNPObject(np_object);
     95   if (!obj)
     96     return false;
     97 
     98   PPVarArrayFromNPVariantArray args(obj->instance(), argc, argv);
     99   PPResultAndExceptionToNPResult result_converter(obj->GetNPObject(), result);
    100 
    101   // For the OOP plugin case we need to grab a reference on the plugin module
    102   // object to ensure that it is not destroyed courtsey an incoming
    103   // ExecuteScript call which destroys the plugin module and in turn the
    104   // dispatcher.
    105   scoped_refptr<PluginModule> ref(obj->instance()->module());
    106 
    107   result_converter.SetResult(obj->ppp_class()->Call(
    108       obj->ppp_class_data(), PP_MakeUndefined(), argc, args.array(),
    109       result_converter.exception()));
    110   return result_converter.success();
    111 }
    112 
    113 bool WrapperClass_HasProperty(NPObject* object, NPIdentifier property_name) {
    114   NPObjectAccessorWithIdentifier accessor(object, property_name, true);
    115   if (!accessor.is_valid())
    116     return false;
    117 
    118   PPResultAndExceptionToNPResult result_converter(
    119       accessor.object()->GetNPObject(), NULL);
    120   bool rv = accessor.object()->ppp_class()->HasProperty(
    121       accessor.object()->ppp_class_data(), accessor.identifier(),
    122       result_converter.exception());
    123   result_converter.CheckExceptionForNoResult();
    124   return rv;
    125 }
    126 
    127 bool WrapperClass_GetProperty(NPObject* object, NPIdentifier property_name,
    128                               NPVariant* result) {
    129   NPObjectAccessorWithIdentifier accessor(object, property_name, true);
    130   if (!accessor.is_valid())
    131     return false;
    132 
    133   PPResultAndExceptionToNPResult result_converter(
    134       accessor.object()->GetNPObject(), result);
    135   return result_converter.SetResult(accessor.object()->ppp_class()->GetProperty(
    136       accessor.object()->ppp_class_data(), accessor.identifier(),
    137       result_converter.exception()));
    138 }
    139 
    140 bool WrapperClass_SetProperty(NPObject* object, NPIdentifier property_name,
    141                               const NPVariant* value) {
    142   NPObjectAccessorWithIdentifier accessor(object, property_name, true);
    143   if (!accessor.is_valid())
    144     return false;
    145 
    146   PPResultAndExceptionToNPResult result_converter(
    147       accessor.object()->GetNPObject(), NULL);
    148   PP_Var value_var = NPVariantToPPVar(accessor.object()->instance(), value);
    149   accessor.object()->ppp_class()->SetProperty(
    150       accessor.object()->ppp_class_data(), accessor.identifier(), value_var,
    151       result_converter.exception());
    152   PpapiGlobals::Get()->GetVarTracker()->ReleaseVar(value_var);
    153   return result_converter.CheckExceptionForNoResult();
    154 }
    155 
    156 bool WrapperClass_RemoveProperty(NPObject* object, NPIdentifier property_name) {
    157   NPObjectAccessorWithIdentifier accessor(object, property_name, true);
    158   if (!accessor.is_valid())
    159     return false;
    160 
    161   PPResultAndExceptionToNPResult result_converter(
    162       accessor.object()->GetNPObject(), NULL);
    163   accessor.object()->ppp_class()->RemoveProperty(
    164       accessor.object()->ppp_class_data(), accessor.identifier(),
    165       result_converter.exception());
    166   return result_converter.CheckExceptionForNoResult();
    167 }
    168 
    169 bool WrapperClass_Enumerate(NPObject* object, NPIdentifier** values,
    170                             uint32_t* count) {
    171   *values = NULL;
    172   *count = 0;
    173   PluginObject* obj = PluginObject::FromNPObject(object);
    174   if (!obj)
    175     return false;
    176 
    177   uint32_t property_count = 0;
    178   PP_Var* properties = NULL;  // Must be freed!
    179   PPResultAndExceptionToNPResult result_converter(obj->GetNPObject(), NULL);
    180   obj->ppp_class()->GetAllPropertyNames(obj->ppp_class_data(),
    181                                         &property_count, &properties,
    182                                         result_converter.exception());
    183 
    184   // Convert the array of PP_Var to an array of NPIdentifiers. If any
    185   // conversions fail, we will set the exception.
    186   if (!result_converter.has_exception()) {
    187     if (property_count > 0) {
    188       *values = static_cast<NPIdentifier*>(
    189           malloc(sizeof(NPIdentifier) * property_count));
    190       *count = 0;  // Will be the number of items successfully converted.
    191       for (uint32_t i = 0; i < property_count; ++i) {
    192         if (!((*values)[i] = PPVarToNPIdentifier(properties[i]))) {
    193           // Throw an exception for the failed convertion.
    194           *result_converter.exception() =
    195               StringVar::StringToPPVar(kInvalidValueException);
    196           break;
    197         }
    198         (*count)++;
    199       }
    200 
    201       if (result_converter.has_exception()) {
    202         // We don't actually have to free the identifiers we converted since
    203         // all identifiers leak anyway :( .
    204         free(*values);
    205         *values = NULL;
    206         *count = 0;
    207       }
    208     }
    209   }
    210 
    211   // This will actually throw the exception, either from GetAllPropertyNames,
    212   // or if anything was set during the conversion process.
    213   result_converter.CheckExceptionForNoResult();
    214 
    215   // Release the PP_Var that the plugin allocated. On success, they will all
    216   // be converted to NPVariants, and on failure, we want them to just go away.
    217   ppapi::VarTracker* var_tracker = PpapiGlobals::Get()->GetVarTracker();
    218   for (uint32_t i = 0; i < property_count; ++i)
    219     var_tracker->ReleaseVar(properties[i]);
    220   free(properties);
    221   return result_converter.success();
    222 }
    223 
    224 bool WrapperClass_Construct(NPObject* object, const NPVariant* argv,
    225                             uint32_t argc, NPVariant* result) {
    226   PluginObject* obj = PluginObject::FromNPObject(object);
    227   if (!obj)
    228     return false;
    229 
    230   PPVarArrayFromNPVariantArray args(obj->instance(), argc, argv);
    231   PPResultAndExceptionToNPResult result_converter(obj->GetNPObject(), result);
    232   return result_converter.SetResult(obj->ppp_class()->Construct(
    233       obj->ppp_class_data(), argc, args.array(),
    234       result_converter.exception()));
    235 }
    236 
    237 const NPClass wrapper_class = {
    238   NP_CLASS_STRUCT_VERSION,
    239   WrapperClass_Allocate,
    240   WrapperClass_Deallocate,
    241   WrapperClass_Invalidate,
    242   WrapperClass_HasMethod,
    243   WrapperClass_Invoke,
    244   WrapperClass_InvokeDefault,
    245   WrapperClass_HasProperty,
    246   WrapperClass_GetProperty,
    247   WrapperClass_SetProperty,
    248   WrapperClass_RemoveProperty,
    249   WrapperClass_Enumerate,
    250   WrapperClass_Construct
    251 };
    252 
    253 }  // namespace
    254 
    255 // PluginObject ----------------------------------------------------------------
    256 
    257 struct PluginObject::NPObjectWrapper : public NPObject {
    258   // Points to the var object that owns this wrapper. This value may be NULL
    259   // if there is no var owning this wrapper. This can happen if the plugin
    260   // releases all references to the var, but a reference to the underlying
    261   // NPObject is still held by script on the page.
    262   PluginObject* obj;
    263 };
    264 
    265 PluginObject::PluginObject(PepperPluginInstanceImpl* instance,
    266                            NPObjectWrapper* object_wrapper,
    267                            const PPP_Class_Deprecated* ppp_class,
    268                            void* ppp_class_data)
    269     : instance_(instance),
    270       object_wrapper_(object_wrapper),
    271       ppp_class_(ppp_class),
    272       ppp_class_data_(ppp_class_data) {
    273   // Make the object wrapper refer back to this class so our NPObject
    274   // implementation can call back into the Pepper layer.
    275   object_wrapper_->obj = this;
    276   instance_->AddPluginObject(this);
    277 }
    278 
    279 PluginObject::~PluginObject() {
    280   // The wrapper we made for this NPObject may still have a reference to it
    281   // from JavaScript, so we clear out its ObjectVar back pointer which will
    282   // cause all calls "up" to the plugin to become NOPs. Our ObjectVar base
    283   // class will release our reference to the object, which may or may not
    284   // delete the NPObject.
    285   DCHECK(object_wrapper_->obj == this);
    286   object_wrapper_->obj = NULL;
    287   instance_->RemovePluginObject(this);
    288 }
    289 
    290 PP_Var PluginObject::Create(PepperPluginInstanceImpl* instance,
    291                             const PPP_Class_Deprecated* ppp_class,
    292                             void* ppp_class_data) {
    293   // This will internally end up calling our AllocateObjectWrapper via the
    294   // WrapperClass_Allocated function which will have created an object wrapper
    295   // appropriate for this class (derived from NPObject).
    296   NPObjectWrapper* wrapper = static_cast<NPObjectWrapper*>(
    297       WebBindings::createObject(instance->instanceNPP(),
    298       const_cast<NPClass*>(&wrapper_class)));
    299 
    300   // This object will register itself both with the NPObject and with the
    301   // PluginModule. The NPObject will normally handle its lifetime, and it
    302   // will get deleted in the destroy method. It may also get deleted when the
    303   // plugin module is deallocated.
    304   new PluginObject(instance, wrapper, ppp_class, ppp_class_data);
    305 
    306   // We can just use a normal ObjectVar to refer to this object from the
    307   // plugin. It will hold a ref to the underlying NPObject which will in turn
    308   // hold our pluginObject.
    309   PP_Var obj_var(NPObjectToPPVar(instance, wrapper));
    310 
    311   // Note that the ObjectVar constructor incremented the reference count, and so
    312   // did WebBindings::createObject above. Now that the PP_Var has taken
    313   // ownership, we need to release to balance out the createObject reference
    314   // count bump.
    315   WebBindings::releaseObject(wrapper);
    316   return obj_var;
    317 }
    318 
    319 NPObject* PluginObject::GetNPObject() const {
    320   return object_wrapper_;
    321 }
    322 
    323 // static
    324 bool PluginObject::IsInstanceOf(NPObject* np_object,
    325                                 const PPP_Class_Deprecated* ppp_class,
    326                                 void** ppp_class_data) {
    327   // Validate that this object is implemented by our wrapper class before
    328   // trying to get the PluginObject.
    329   if (np_object->_class != &wrapper_class)
    330     return false;
    331 
    332   PluginObject* plugin_object = FromNPObject(np_object);
    333   if (!plugin_object)
    334     return false;  // Object is no longer alive.
    335 
    336   if (plugin_object->ppp_class() != ppp_class)
    337     return false;
    338   if (ppp_class_data)
    339     *ppp_class_data = plugin_object->ppp_class_data();
    340   return true;
    341 }
    342 
    343 // static
    344 PluginObject* PluginObject::FromNPObject(NPObject* object) {
    345   return static_cast<NPObjectWrapper*>(object)->obj;
    346 }
    347 
    348 // static
    349 NPObject* PluginObject::AllocateObjectWrapper() {
    350   NPObjectWrapper* wrapper = new NPObjectWrapper;
    351   memset(wrapper, 0, sizeof(NPObjectWrapper));
    352   return wrapper;
    353 }
    354 
    355 }  // namespace content
    356 
    357