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