Home | History | Annotate | Download | only in npapi
      1 // Copyright 2013 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/child/npapi/npobject_proxy.h"
      6 
      7 #include "content/child/npapi/np_channel_base.h"
      8 #include "content/child/npapi/npobject_util.h"
      9 #include "content/child/plugin_messages.h"
     10 #include "third_party/WebKit/public/web/WebBindings.h"
     11 
     12 #if defined(ENABLE_PLUGINS)
     13 #include "content/child/npapi/plugin_instance.h"
     14 #endif
     15 
     16 using blink::WebBindings;
     17 
     18 namespace content {
     19 
     20 struct NPObjectWrapper {
     21     NPObject object;
     22     NPObjectProxy* proxy;
     23 };
     24 
     25 NPClass NPObjectProxy::npclass_proxy_ = {
     26   NP_CLASS_STRUCT_VERSION,
     27   NPObjectProxy::NPAllocate,
     28   NPObjectProxy::NPDeallocate,
     29   NPObjectProxy::NPPInvalidate,
     30   NPObjectProxy::NPHasMethod,
     31   NPObjectProxy::NPInvoke,
     32   NPObjectProxy::NPInvokeDefault,
     33   NPObjectProxy::NPHasProperty,
     34   NPObjectProxy::NPGetProperty,
     35   NPObjectProxy::NPSetProperty,
     36   NPObjectProxy::NPRemoveProperty,
     37   NPObjectProxy::NPNEnumerate,
     38   NPObjectProxy::NPNConstruct
     39 };
     40 
     41 NPObjectProxy* NPObjectProxy::GetProxy(NPObject* object) {
     42   NPObjectProxy* proxy = NULL;
     43 
     44   // Wrapper exists only for NPObjects that we had created.
     45   if (&npclass_proxy_ == object->_class) {
     46     NPObjectWrapper* wrapper = reinterpret_cast<NPObjectWrapper*>(object);
     47     proxy = wrapper->proxy;
     48   }
     49 
     50   return proxy;
     51 }
     52 
     53 NPObject* NPObjectProxy::GetUnderlyingNPObject() {
     54   return NULL;
     55 }
     56 
     57 IPC::Listener* NPObjectProxy::GetChannelListener() {
     58   return static_cast<IPC::Listener*>(this);
     59 }
     60 
     61 NPObjectProxy::NPObjectProxy(
     62     NPChannelBase* channel,
     63     int route_id,
     64     int render_view_id,
     65     const GURL& page_url)
     66     : channel_(channel),
     67       route_id_(route_id),
     68       render_view_id_(render_view_id),
     69       page_url_(page_url) {
     70   channel_->AddRoute(route_id, this, this);
     71 }
     72 
     73 NPObjectProxy::~NPObjectProxy() {
     74   if (channel_.get()) {
     75     // This NPObjectProxy instance is now invalid and should not be reused for
     76     // requests initiated by plugins. We may receive requests for the
     77     // same NPObject in the context of the outgoing NPObjectMsg_Release call.
     78     // We should be creating new NPObjectProxy instances to wrap these
     79     // NPObjects.
     80     channel_->RemoveMappingForNPObjectProxy(route_id_);
     81     channel_->RemoveRoute(route_id_);
     82     Send(new NPObjectMsg_Release(route_id_));
     83   }
     84 }
     85 
     86 NPObject* NPObjectProxy::Create(NPChannelBase* channel,
     87                                 int route_id,
     88                                 int render_view_id,
     89                                 const GURL& page_url,
     90                                 NPP owner) {
     91   NPObjectWrapper* obj = reinterpret_cast<NPObjectWrapper*>(
     92       WebBindings::createObject(owner, &npclass_proxy_));
     93   obj->proxy = new NPObjectProxy(channel, route_id, render_view_id, page_url);
     94   channel->AddMappingForNPObjectProxy(route_id, &obj->object);
     95   return reinterpret_cast<NPObject*>(obj);
     96 }
     97 
     98 bool NPObjectProxy::Send(IPC::Message* msg) {
     99   if (channel_.get())
    100     return channel_->Send(msg);
    101 
    102   delete msg;
    103   return false;
    104 }
    105 
    106 NPObject* NPObjectProxy::NPAllocate(NPP, NPClass*) {
    107   return reinterpret_cast<NPObject*>(new NPObjectWrapper);
    108 }
    109 
    110 void NPObjectProxy::NPDeallocate(NPObject* npObj) {
    111   NPObjectWrapper* obj = reinterpret_cast<NPObjectWrapper*>(npObj);
    112   delete obj->proxy;
    113   delete obj;
    114 }
    115 
    116 bool NPObjectProxy::OnMessageReceived(const IPC::Message& msg) {
    117   NOTREACHED();
    118   return false;
    119 }
    120 
    121 void NPObjectProxy::OnChannelError() {
    122   // Release our ref count of the plugin channel object, as it addrefs the
    123   // process.
    124   channel_ = NULL;
    125 }
    126 
    127 bool NPObjectProxy::NPHasMethod(NPObject *obj,
    128                                 NPIdentifier name) {
    129   if (obj == NULL)
    130     return false;
    131 
    132   bool result = false;
    133   NPObjectProxy* proxy = GetProxy(obj);
    134 
    135   if (!proxy) {
    136     return obj->_class->hasMethod(obj, name);
    137   }
    138 
    139   NPIdentifier_Param name_param;
    140   CreateNPIdentifierParam(name, &name_param);
    141 
    142   proxy->Send(new NPObjectMsg_HasMethod(proxy->route_id(), name_param,
    143                                         &result));
    144   return result;
    145 }
    146 
    147 bool NPObjectProxy::NPInvoke(NPObject *obj,
    148                              NPIdentifier name,
    149                              const NPVariant *args,
    150                              uint32_t arg_count,
    151                              NPVariant *result) {
    152   return NPInvokePrivate(0, obj, false, name, args, arg_count, result);
    153 }
    154 
    155 bool NPObjectProxy::NPInvokeDefault(NPObject *npobj,
    156                                     const NPVariant *args,
    157                                     uint32_t arg_count,
    158                                     NPVariant *result) {
    159   return NPInvokePrivate(0, npobj, true, 0, args, arg_count, result);
    160 }
    161 
    162 bool NPObjectProxy::NPInvokePrivate(NPP npp,
    163                                     NPObject *obj,
    164                                     bool is_default,
    165                                     NPIdentifier name,
    166                                     const NPVariant *args,
    167                                     uint32_t arg_count,
    168                                     NPVariant *np_result) {
    169   if (obj == NULL)
    170     return false;
    171 
    172   NPObjectProxy* proxy = GetProxy(obj);
    173   if (!proxy) {
    174     if (is_default) {
    175       return obj->_class->invokeDefault(obj, args, arg_count, np_result);
    176     } else {
    177       return obj->_class->invoke(obj, name, args, arg_count, np_result);
    178     }
    179   }
    180 
    181   bool result = false;
    182   int render_view_id = proxy->render_view_id_;
    183   NPIdentifier_Param name_param;
    184   if (is_default) {
    185     // The data won't actually get used, but set it so we don't send random
    186     // data.
    187     name_param.identifier = NULL;
    188   } else {
    189     CreateNPIdentifierParam(name, &name_param);
    190   }
    191 
    192   // Note: This instance can get destroyed in the context of
    193   // Send so addref the channel in this scope.
    194   scoped_refptr<NPChannelBase> channel_copy = proxy->channel_;
    195   std::vector<NPVariant_Param> args_param;
    196   for (unsigned int i = 0; i < arg_count; ++i) {
    197     NPVariant_Param param;
    198     CreateNPVariantParam(args[i],
    199                          channel_copy.get(),
    200                          &param,
    201                          false,
    202                          render_view_id,
    203                          proxy->page_url_);
    204     args_param.push_back(param);
    205   }
    206 
    207   NPVariant_Param param_result;
    208   NPObjectMsg_Invoke* msg = new NPObjectMsg_Invoke(
    209       proxy->route_id_, is_default, name_param, args_param, &param_result,
    210       &result);
    211 
    212   // If we're in the plugin process and this invoke leads to a dialog box, the
    213   // plugin will hang the window hierarchy unless we pump the window message
    214   // queue while waiting for a reply.  We need to do this to simulate what
    215   // happens when everything runs in-process (while calling MessageBox window
    216   // messages are pumped).
    217   if (IsPluginProcess() && proxy->channel()) {
    218     msg->set_pump_messages_event(
    219         proxy->channel()->GetModalDialogEvent(render_view_id));
    220   }
    221 
    222   GURL page_url = proxy->page_url_;
    223   proxy->Send(msg);
    224 
    225   // Send may delete proxy.
    226   proxy = NULL;
    227 
    228   if (!result)
    229     return false;
    230 
    231   CreateNPVariant(
    232       param_result, channel_copy.get(), np_result, render_view_id, page_url);
    233   return true;
    234 }
    235 
    236 bool NPObjectProxy::NPHasProperty(NPObject *obj,
    237                                   NPIdentifier name) {
    238   if (obj == NULL)
    239     return false;
    240 
    241   bool result = false;
    242   NPObjectProxy* proxy = GetProxy(obj);
    243   if (!proxy) {
    244     return obj->_class->hasProperty(obj, name);
    245   }
    246 
    247   NPIdentifier_Param name_param;
    248   CreateNPIdentifierParam(name, &name_param);
    249 
    250   NPVariant_Param param;
    251   proxy->Send(new NPObjectMsg_HasProperty(
    252       proxy->route_id(), name_param, &result));
    253 
    254   // Send may delete proxy.
    255   proxy = NULL;
    256 
    257   return result;
    258 }
    259 
    260 bool NPObjectProxy::NPGetProperty(NPObject *obj,
    261                                   NPIdentifier name,
    262                                   NPVariant *np_result) {
    263   // Please refer to http://code.google.com/p/chromium/issues/detail?id=2556,
    264   // which was a crash in the XStandard plugin during plugin shutdown. The
    265   // crash occured because the plugin requests the plugin script object,
    266   // which fails. The plugin does not check the result of the operation and
    267   // invokes NPN_GetProperty on a NULL object which lead to the crash. If
    268   // we observe similar crashes in other methods in the future, these null
    269   // checks may have to be replicated in the other methods in this class.
    270   if (obj == NULL)
    271     return false;
    272 
    273   NPObjectProxy* proxy = GetProxy(obj);
    274   if (!proxy) {
    275     return obj->_class->getProperty(obj, name, np_result);
    276   }
    277 
    278   bool result = false;
    279   int render_view_id = proxy->render_view_id_;
    280   NPIdentifier_Param name_param;
    281   CreateNPIdentifierParam(name, &name_param);
    282 
    283   NPVariant_Param param;
    284   scoped_refptr<NPChannelBase> channel(proxy->channel_);
    285 
    286   GURL page_url = proxy->page_url_;
    287   proxy->Send(new NPObjectMsg_GetProperty(
    288       proxy->route_id(), name_param, &param, &result));
    289   // Send may delete proxy.
    290   proxy = NULL;
    291   if (!result)
    292     return false;
    293 
    294   CreateNPVariant(
    295       param, channel.get(), np_result, render_view_id, page_url);
    296 
    297   return true;
    298 }
    299 
    300 bool NPObjectProxy::NPSetProperty(NPObject *obj,
    301                                   NPIdentifier name,
    302                                   const NPVariant *value) {
    303   if (obj == NULL)
    304     return false;
    305 
    306   NPObjectProxy* proxy = GetProxy(obj);
    307   if (!proxy) {
    308     return obj->_class->setProperty(obj, name, value);
    309   }
    310 
    311   bool result = false;
    312   int render_view_id = proxy->render_view_id_;
    313   NPIdentifier_Param name_param;
    314   CreateNPIdentifierParam(name, &name_param);
    315 
    316   NPVariant_Param value_param;
    317   CreateNPVariantParam(
    318       *value, proxy->channel(), &value_param, false, render_view_id,
    319       proxy->page_url_);
    320 
    321   proxy->Send(new NPObjectMsg_SetProperty(
    322       proxy->route_id(), name_param, value_param, &result));
    323   // Send may delete proxy.
    324   proxy = NULL;
    325 
    326   return result;
    327 }
    328 
    329 bool NPObjectProxy::NPRemoveProperty(NPObject *obj,
    330                                      NPIdentifier name) {
    331   if (obj == NULL)
    332     return false;
    333 
    334   bool result = false;
    335   NPObjectProxy* proxy = GetProxy(obj);
    336   if (!proxy) {
    337     return obj->_class->removeProperty(obj, name);
    338   }
    339 
    340   NPIdentifier_Param name_param;
    341   CreateNPIdentifierParam(name, &name_param);
    342 
    343   NPVariant_Param param;
    344   proxy->Send(new NPObjectMsg_RemoveProperty(
    345       proxy->route_id(), name_param, &result));
    346   // Send may delete proxy.
    347   proxy = NULL;
    348 
    349   return result;
    350 }
    351 
    352 void NPObjectProxy::NPPInvalidate(NPObject *obj) {
    353   if (obj == NULL)
    354     return;
    355 
    356   NPObjectProxy* proxy = GetProxy(obj);
    357   if (!proxy) {
    358     obj->_class->invalidate(obj);
    359     return;
    360   }
    361 
    362   proxy->Send(new NPObjectMsg_Invalidate(proxy->route_id()));
    363   // Send may delete proxy.
    364   proxy = NULL;
    365 }
    366 
    367 bool NPObjectProxy::NPNEnumerate(NPObject *obj,
    368                                  NPIdentifier **value,
    369                                  uint32_t *count) {
    370   if (obj == NULL)
    371     return false;
    372 
    373   bool result = false;
    374   NPObjectProxy* proxy = GetProxy(obj);
    375   if (!proxy) {
    376     if (obj->_class->structVersion >= NP_CLASS_STRUCT_VERSION_ENUM) {
    377       return obj->_class->enumerate(obj, value, count);
    378     } else {
    379       return false;
    380     }
    381   }
    382 
    383   std::vector<NPIdentifier_Param> value_param;
    384   proxy->Send(new NPObjectMsg_Enumeration(
    385       proxy->route_id(), &value_param, &result));
    386   // Send may delete proxy.
    387   proxy = NULL;
    388 
    389   if (!result)
    390     return false;
    391 
    392   *count = static_cast<unsigned int>(value_param.size());
    393   *value = static_cast<NPIdentifier *>(malloc(sizeof(NPIdentifier) * *count));
    394   for (unsigned int i = 0; i < *count; ++i)
    395     (*value)[i] = CreateNPIdentifier(value_param[i]);
    396 
    397   return true;
    398 }
    399 
    400 bool NPObjectProxy::NPNConstruct(NPObject *obj,
    401                                  const NPVariant *args,
    402                                  uint32_t arg_count,
    403                                  NPVariant *np_result) {
    404   if (obj == NULL)
    405     return false;
    406 
    407   NPObjectProxy* proxy = GetProxy(obj);
    408   if (!proxy) {
    409     if (obj->_class->structVersion >= NP_CLASS_STRUCT_VERSION_CTOR) {
    410       return obj->_class->construct(obj, args, arg_count, np_result);
    411     } else {
    412       return false;
    413     }
    414   }
    415 
    416   bool result = false;
    417   int render_view_id = proxy->render_view_id_;
    418 
    419   // Note: This instance can get destroyed in the context of
    420   // Send so addref the channel in this scope.
    421   scoped_refptr<NPChannelBase> channel_copy = proxy->channel_;
    422   std::vector<NPVariant_Param> args_param;
    423   for (unsigned int i = 0; i < arg_count; ++i) {
    424     NPVariant_Param param;
    425     CreateNPVariantParam(args[i],
    426                          channel_copy.get(),
    427                          &param,
    428                          false,
    429                          render_view_id,
    430                          proxy->page_url_);
    431     args_param.push_back(param);
    432   }
    433 
    434   NPVariant_Param param_result;
    435   NPObjectMsg_Construct* msg = new NPObjectMsg_Construct(
    436       proxy->route_id_, args_param, &param_result, &result);
    437 
    438   // See comment in NPObjectProxy::NPInvokePrivate.
    439   if (IsPluginProcess() && proxy->channel()) {
    440     msg->set_pump_messages_event(
    441         proxy->channel()->GetModalDialogEvent(proxy->render_view_id_));
    442   }
    443 
    444   GURL page_url = proxy->page_url_;
    445   proxy->Send(msg);
    446 
    447   // Send may delete proxy.
    448   proxy = NULL;
    449 
    450   if (!result)
    451     return false;
    452 
    453   CreateNPVariant(
    454       param_result, channel_copy.get(), np_result, render_view_id, page_url);
    455   return true;
    456 }
    457 
    458 bool NPObjectProxy::NPNEvaluate(NPP npp,
    459                                 NPObject *obj,
    460                                 NPString *script,
    461                                 NPVariant *result_var) {
    462   NPObjectProxy* proxy = GetProxy(obj);
    463   if (!proxy) {
    464     return false;
    465   }
    466 
    467   bool result = false;
    468   int render_view_id = proxy->render_view_id_;
    469   bool popups_allowed = false;
    470 
    471 #if defined(ENABLE_PLUGINS)
    472   if (npp) {
    473     PluginInstance* plugin_instance =
    474         reinterpret_cast<PluginInstance*>(npp->ndata);
    475     if (plugin_instance)
    476       popups_allowed = plugin_instance->popups_allowed();
    477   }
    478 #endif
    479 
    480   NPVariant_Param result_param;
    481   std::string script_str = std::string(
    482       script->UTF8Characters, script->UTF8Length);
    483 
    484   NPObjectMsg_Evaluate* msg = new NPObjectMsg_Evaluate(proxy->route_id(),
    485                                                        script_str,
    486                                                        popups_allowed,
    487                                                        &result_param,
    488                                                        &result);
    489 
    490   // See comment in NPObjectProxy::NPInvokePrivate.
    491   if (IsPluginProcess() && proxy->channel()) {
    492     msg->set_pump_messages_event(
    493         proxy->channel()->GetModalDialogEvent(render_view_id));
    494   }
    495   scoped_refptr<NPChannelBase> channel(proxy->channel_);
    496 
    497   GURL page_url = proxy->page_url_;
    498   proxy->Send(msg);
    499   // Send may delete proxy.
    500   proxy = NULL;
    501   if (!result)
    502     return false;
    503 
    504   CreateNPVariant(
    505       result_param, channel.get(), result_var, render_view_id, page_url);
    506   return true;
    507 }
    508 
    509 }  // namespace content
    510