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
16using blink::WebBindings;
17
18namespace content {
19
20struct NPObjectWrapper {
21    NPObject object;
22    NPObjectProxy* proxy;
23};
24
25NPClass 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
41NPObjectProxy* 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
53NPObject* NPObjectProxy::GetUnderlyingNPObject() {
54  return NULL;
55}
56
57IPC::Listener* NPObjectProxy::GetChannelListener() {
58  return static_cast<IPC::Listener*>(this);
59}
60
61NPObjectProxy::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
73NPObjectProxy::~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
86NPObject* 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
98bool NPObjectProxy::Send(IPC::Message* msg) {
99  if (channel_.get())
100    return channel_->Send(msg);
101
102  delete msg;
103  return false;
104}
105
106NPObject* NPObjectProxy::NPAllocate(NPP, NPClass*) {
107  return reinterpret_cast<NPObject*>(new NPObjectWrapper);
108}
109
110void NPObjectProxy::NPDeallocate(NPObject* npObj) {
111  NPObjectWrapper* obj = reinterpret_cast<NPObjectWrapper*>(npObj);
112  delete obj->proxy;
113  delete obj;
114}
115
116bool NPObjectProxy::OnMessageReceived(const IPC::Message& msg) {
117  NOTREACHED();
118  return false;
119}
120
121void NPObjectProxy::OnChannelError() {
122  // Release our ref count of the plugin channel object, as it addrefs the
123  // process.
124  channel_ = NULL;
125}
126
127bool 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
147bool 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
155bool 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
162bool 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
236bool 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
260bool 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
300bool 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
329bool 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
352void 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
367bool 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
400bool 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
458bool 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