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
GetProxy(NPObject * object)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
GetUnderlyingNPObject()53 NPObject* NPObjectProxy::GetUnderlyingNPObject() {
54 return NULL;
55 }
56
GetChannelListener()57 IPC::Listener* NPObjectProxy::GetChannelListener() {
58 return static_cast<IPC::Listener*>(this);
59 }
60
NPObjectProxy(NPChannelBase * channel,int route_id,int render_view_id,const GURL & page_url)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
~NPObjectProxy()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
Create(NPChannelBase * channel,int route_id,int render_view_id,const GURL & page_url,NPP owner)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
Send(IPC::Message * msg)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
NPAllocate(NPP,NPClass *)106 NPObject* NPObjectProxy::NPAllocate(NPP, NPClass*) {
107 return reinterpret_cast<NPObject*>(new NPObjectWrapper);
108 }
109
NPDeallocate(NPObject * npObj)110 void NPObjectProxy::NPDeallocate(NPObject* npObj) {
111 NPObjectWrapper* obj = reinterpret_cast<NPObjectWrapper*>(npObj);
112 delete obj->proxy;
113 delete obj;
114 }
115
OnMessageReceived(const IPC::Message & msg)116 bool NPObjectProxy::OnMessageReceived(const IPC::Message& msg) {
117 NOTREACHED();
118 return false;
119 }
120
OnChannelError()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
NPHasMethod(NPObject * obj,NPIdentifier name)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
NPInvoke(NPObject * obj,NPIdentifier name,const NPVariant * args,uint32_t arg_count,NPVariant * result)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
NPInvokeDefault(NPObject * npobj,const NPVariant * args,uint32_t arg_count,NPVariant * result)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
NPInvokePrivate(NPP npp,NPObject * obj,bool is_default,NPIdentifier name,const NPVariant * args,uint32_t arg_count,NPVariant * np_result)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 ¶m,
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, ¶m_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
NPHasProperty(NPObject * obj,NPIdentifier name)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
NPGetProperty(NPObject * obj,NPIdentifier name,NPVariant * np_result)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, ¶m, &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
NPSetProperty(NPObject * obj,NPIdentifier name,const NPVariant * value)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
NPRemoveProperty(NPObject * obj,NPIdentifier name)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
NPPInvalidate(NPObject * obj)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
NPNEnumerate(NPObject * obj,NPIdentifier ** value,uint32_t * count)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
NPNConstruct(NPObject * obj,const NPVariant * args,uint32_t arg_count,NPVariant * np_result)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 ¶m,
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, ¶m_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
NPNEvaluate(NPP npp,NPObject * obj,NPString * script,NPVariant * result_var)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