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_stub.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 "content/public/common/content_client.h"
11 #include "content/public/common/content_switches.h"
12 #include "third_party/WebKit/public/web/WebBindings.h"
13 #include "third_party/npapi/bindings/npapi.h"
14 #include "third_party/npapi/bindings/npruntime.h"
15
16 #if defined(OS_WIN)
17 #include "base/command_line.h"
18 #include "content/common/plugin_constants_win.h"
19 #endif
20
21 using blink::WebBindings;
22
23 namespace content {
24
NPObjectStub(NPObject * npobject,NPChannelBase * channel,int route_id,int render_view_id,const GURL & page_url)25 NPObjectStub::NPObjectStub(
26 NPObject* npobject,
27 NPChannelBase* channel,
28 int route_id,
29 int render_view_id,
30 const GURL& page_url)
31 : npobject_(npobject),
32 channel_(channel),
33 route_id_(route_id),
34 render_view_id_(render_view_id),
35 page_url_(page_url) {
36 channel_->AddMappingForNPObjectStub(route_id, npobject);
37 channel_->AddRoute(route_id, this, this);
38
39 // We retain the object just as PluginHost does if everything was in-process.
40 WebBindings::retainObject(npobject_);
41 }
42
~NPObjectStub()43 NPObjectStub::~NPObjectStub() {
44 channel_->RemoveRoute(route_id_);
45 DCHECK(!npobject_);
46 }
47
DeleteSoon()48 void NPObjectStub::DeleteSoon() {
49 if (npobject_) {
50 channel_->RemoveMappingForNPObjectStub(route_id_, npobject_);
51
52 // We need to NULL npobject_ prior to calling releaseObject() to avoid
53 // problems with re-entrancy. See http://crbug.com/94179#c17 for more
54 // details on how this can happen.
55 NPObject* npobject = npobject_;
56 npobject_ = NULL;
57
58 WebBindings::releaseObject(npobject);
59
60 base::MessageLoop::current()->DeleteSoon(FROM_HERE, this);
61 }
62 }
63
Send(IPC::Message * msg)64 bool NPObjectStub::Send(IPC::Message* msg) {
65 return channel_->Send(msg);
66 }
67
GetUnderlyingNPObject()68 NPObject* NPObjectStub::GetUnderlyingNPObject() {
69 return npobject_;
70 }
71
GetChannelListener()72 IPC::Listener* NPObjectStub::GetChannelListener() {
73 return static_cast<IPC::Listener*>(this);
74 }
75
OnMessageReceived(const IPC::Message & msg)76 bool NPObjectStub::OnMessageReceived(const IPC::Message& msg) {
77 GetContentClient()->SetActiveURL(page_url_);
78 if (!npobject_) {
79 if (msg.is_sync()) {
80 // The object could be garbage because the frame has gone away, so
81 // just send an error reply to the caller.
82 IPC::Message* reply = IPC::SyncMessage::GenerateReply(&msg);
83 reply->set_reply_error();
84 Send(reply);
85 }
86
87 return true;
88 }
89
90 bool handled = true;
91 IPC_BEGIN_MESSAGE_MAP(NPObjectStub, msg)
92 IPC_MESSAGE_HANDLER_DELAY_REPLY(NPObjectMsg_Release, OnRelease);
93 IPC_MESSAGE_HANDLER(NPObjectMsg_HasMethod, OnHasMethod);
94 IPC_MESSAGE_HANDLER_DELAY_REPLY(NPObjectMsg_Invoke, OnInvoke);
95 IPC_MESSAGE_HANDLER(NPObjectMsg_HasProperty, OnHasProperty);
96 IPC_MESSAGE_HANDLER(NPObjectMsg_GetProperty, OnGetProperty);
97 IPC_MESSAGE_HANDLER_DELAY_REPLY(NPObjectMsg_SetProperty, OnSetProperty);
98 IPC_MESSAGE_HANDLER(NPObjectMsg_RemoveProperty, OnRemoveProperty);
99 IPC_MESSAGE_HANDLER(NPObjectMsg_Invalidate, OnInvalidate);
100 IPC_MESSAGE_HANDLER(NPObjectMsg_Enumeration, OnEnumeration);
101 IPC_MESSAGE_HANDLER_DELAY_REPLY(NPObjectMsg_Construct, OnConstruct);
102 IPC_MESSAGE_HANDLER_DELAY_REPLY(NPObjectMsg_Evaluate, OnEvaluate);
103 IPC_MESSAGE_UNHANDLED(handled = false)
104 IPC_END_MESSAGE_MAP()
105 DCHECK(handled);
106 return handled;
107 }
108
OnChannelError()109 void NPObjectStub::OnChannelError() {
110 DeleteSoon();
111 }
112
OnRelease(IPC::Message * reply_msg)113 void NPObjectStub::OnRelease(IPC::Message* reply_msg) {
114 Send(reply_msg);
115 DeleteSoon();
116 }
117
OnHasMethod(const NPIdentifier_Param & name,bool * result)118 void NPObjectStub::OnHasMethod(const NPIdentifier_Param& name,
119 bool* result) {
120 NPIdentifier id = CreateNPIdentifier(name);
121 // If we're in the plugin process, then the stub is holding onto an NPObject
122 // from the plugin, so all function calls on it need to go through the
123 // functions in NPClass. If we're in the renderer process, then we just call
124 // the NPN_ functions.
125 if (IsPluginProcess()) {
126 if (npobject_->_class->hasMethod) {
127 *result = npobject_->_class->hasMethod(npobject_, id);
128 } else {
129 *result = false;
130 }
131 } else {
132 *result = WebBindings::hasMethod(0, npobject_, id);
133 }
134 }
135
OnInvoke(bool is_default,const NPIdentifier_Param & method,const std::vector<NPVariant_Param> & args,IPC::Message * reply_msg)136 void NPObjectStub::OnInvoke(bool is_default,
137 const NPIdentifier_Param& method,
138 const std::vector<NPVariant_Param>& args,
139 IPC::Message* reply_msg) {
140 bool return_value = false;
141 NPVariant_Param result_param;
142 NPVariant result_var;
143
144 VOID_TO_NPVARIANT(result_var);
145 result_param.type = NPVARIANT_PARAM_VOID;
146
147 int arg_count = static_cast<int>(args.size());
148 NPVariant* args_var = new NPVariant[arg_count];
149 for (int i = 0; i < arg_count; ++i) {
150 if (!CreateNPVariant(args[i],
151 channel_.get(),
152 &(args_var[i]),
153 render_view_id_,
154 page_url_)) {
155 NPObjectMsg_Invoke::WriteReplyParams(
156 reply_msg, result_param, return_value);
157 channel_->Send(reply_msg);
158 delete[] args_var;
159 return;
160 }
161 }
162
163 if (is_default) {
164 if (IsPluginProcess()) {
165 if (npobject_->_class->invokeDefault) {
166 return_value = npobject_->_class->invokeDefault(
167 npobject_, args_var, arg_count, &result_var);
168 } else {
169 return_value = false;
170 }
171 } else {
172 return_value = WebBindings::invokeDefault(
173 0, npobject_, args_var, arg_count, &result_var);
174 }
175 } else {
176 NPIdentifier id = CreateNPIdentifier(method);
177 if (IsPluginProcess()) {
178 if (npobject_->_class->invoke) {
179 return_value = npobject_->_class->invoke(
180 npobject_, id, args_var, arg_count, &result_var);
181 } else {
182 return_value = false;
183 }
184 } else {
185 return_value = WebBindings::invoke(
186 0, npobject_, id, args_var, arg_count, &result_var);
187 }
188 }
189
190 for (int i = 0; i < arg_count; ++i)
191 WebBindings::releaseVariantValue(&(args_var[i]));
192
193 delete[] args_var;
194
195 CreateNPVariantParam(result_var,
196 channel_.get(),
197 &result_param,
198 true,
199 render_view_id_,
200 page_url_);
201 NPObjectMsg_Invoke::WriteReplyParams(reply_msg, result_param, return_value);
202 channel_->Send(reply_msg);
203 }
204
OnHasProperty(const NPIdentifier_Param & name,bool * result)205 void NPObjectStub::OnHasProperty(const NPIdentifier_Param& name,
206 bool* result) {
207 NPIdentifier id = CreateNPIdentifier(name);
208 if (IsPluginProcess()) {
209 if (npobject_->_class->hasProperty) {
210 *result = npobject_->_class->hasProperty(npobject_, id);
211 } else {
212 *result = false;
213 }
214 } else {
215 *result = WebBindings::hasProperty(0, npobject_, id);
216 }
217 }
218
OnGetProperty(const NPIdentifier_Param & name,NPVariant_Param * property,bool * result)219 void NPObjectStub::OnGetProperty(const NPIdentifier_Param& name,
220 NPVariant_Param* property,
221 bool* result) {
222 NPVariant result_var;
223 VOID_TO_NPVARIANT(result_var);
224 NPIdentifier id = CreateNPIdentifier(name);
225
226 if (IsPluginProcess()) {
227 if (npobject_->_class->getProperty) {
228 *result = npobject_->_class->getProperty(npobject_, id, &result_var);
229 } else {
230 *result = false;
231 }
232 } else {
233 *result = WebBindings::getProperty(0, npobject_, id, &result_var);
234 }
235
236 CreateNPVariantParam(
237 result_var, channel_.get(), property, true, render_view_id_, page_url_);
238 }
239
OnSetProperty(const NPIdentifier_Param & name,const NPVariant_Param & property,IPC::Message * reply_msg)240 void NPObjectStub::OnSetProperty(const NPIdentifier_Param& name,
241 const NPVariant_Param& property,
242 IPC::Message* reply_msg) {
243 bool result = false;
244 NPIdentifier id = CreateNPIdentifier(name);
245 NPVariant property_var;
246 if (!CreateNPVariant(property,
247 channel_.get(),
248 &property_var,
249 render_view_id_,
250 page_url_)) {
251 NPObjectMsg_SetProperty::WriteReplyParams(reply_msg, result);
252 channel_->Send(reply_msg);
253 return;
254 }
255
256 if (IsPluginProcess()) {
257 if (npobject_->_class->setProperty) {
258 #if defined(OS_WIN)
259 static base::FilePath plugin_path =
260 base::CommandLine::ForCurrentProcess()->GetSwitchValuePath(
261 switches::kPluginPath);
262 static std::wstring filename = base::StringToLowerASCII(
263 plugin_path.BaseName().value());
264 static NPIdentifier fullscreen =
265 WebBindings::getStringIdentifier("fullScreen");
266 if (filename == kNewWMPPlugin && id == fullscreen) {
267 // Workaround for bug 15985, which is if Flash causes WMP to go
268 // full screen a deadlock can occur when WMP calls SetFocus.
269 NPObjectMsg_SetProperty::WriteReplyParams(reply_msg, true);
270 Send(reply_msg);
271 reply_msg = NULL;
272 }
273 #endif
274 result = npobject_->_class->setProperty(npobject_, id, &property_var);
275 } else {
276 result = false;
277 }
278 } else {
279 result = WebBindings::setProperty(0, npobject_, id, &property_var);
280 }
281
282 WebBindings::releaseVariantValue(&property_var);
283
284 if (reply_msg) {
285 NPObjectMsg_SetProperty::WriteReplyParams(reply_msg, result);
286 Send(reply_msg);
287 }
288 }
289
OnRemoveProperty(const NPIdentifier_Param & name,bool * result)290 void NPObjectStub::OnRemoveProperty(const NPIdentifier_Param& name,
291 bool* result) {
292 NPIdentifier id = CreateNPIdentifier(name);
293 if (IsPluginProcess()) {
294 if (npobject_->_class->removeProperty) {
295 *result = npobject_->_class->removeProperty(npobject_, id);
296 } else {
297 *result = false;
298 }
299 } else {
300 *result = WebBindings::removeProperty(0, npobject_, id);
301 }
302 }
303
OnInvalidate()304 void NPObjectStub::OnInvalidate() {
305 if (!IsPluginProcess()) {
306 NOTREACHED() << "Should only be called on NPObjects in the plugin";
307 return;
308 }
309
310 if (!npobject_->_class->invalidate)
311 return;
312
313 npobject_->_class->invalidate(npobject_);
314 }
315
OnEnumeration(std::vector<NPIdentifier_Param> * value,bool * result)316 void NPObjectStub::OnEnumeration(std::vector<NPIdentifier_Param>* value,
317 bool* result) {
318 NPIdentifier* value_np = NULL;
319 unsigned int count = 0;
320 if (!IsPluginProcess()) {
321 *result = WebBindings::enumerate(0, npobject_, &value_np, &count);
322 } else {
323 if (npobject_->_class->structVersion < NP_CLASS_STRUCT_VERSION_ENUM ||
324 !npobject_->_class->enumerate) {
325 *result = false;
326 return;
327 }
328
329 *result = npobject_->_class->enumerate(npobject_, &value_np, &count);
330 }
331
332 if (!*result)
333 return;
334
335 for (unsigned int i = 0; i < count; ++i) {
336 NPIdentifier_Param param;
337 CreateNPIdentifierParam(value_np[i], ¶m);
338 value->push_back(param);
339 }
340
341 free(value_np);
342 }
343
OnConstruct(const std::vector<NPVariant_Param> & args,IPC::Message * reply_msg)344 void NPObjectStub::OnConstruct(const std::vector<NPVariant_Param>& args,
345 IPC::Message* reply_msg) {
346 bool return_value = false;
347 NPVariant_Param result_param;
348 NPVariant result_var;
349
350 VOID_TO_NPVARIANT(result_var);
351
352 int arg_count = static_cast<int>(args.size());
353 NPVariant* args_var = new NPVariant[arg_count];
354 for (int i = 0; i < arg_count; ++i) {
355 if (!CreateNPVariant(args[i],
356 channel_.get(),
357 &(args_var[i]),
358 render_view_id_,
359 page_url_)) {
360 NPObjectMsg_Invoke::WriteReplyParams(
361 reply_msg, result_param, return_value);
362 channel_->Send(reply_msg);
363 delete[] args_var;
364 return;
365 }
366 }
367
368 if (IsPluginProcess()) {
369 if (npobject_->_class->structVersion >= NP_CLASS_STRUCT_VERSION_CTOR &&
370 npobject_->_class->construct) {
371 return_value = npobject_->_class->construct(
372 npobject_, args_var, arg_count, &result_var);
373 } else {
374 return_value = false;
375 }
376 } else {
377 return_value = WebBindings::construct(
378 0, npobject_, args_var, arg_count, &result_var);
379 }
380
381 for (int i = 0; i < arg_count; ++i)
382 WebBindings::releaseVariantValue(&(args_var[i]));
383
384 delete[] args_var;
385
386 CreateNPVariantParam(result_var,
387 channel_.get(),
388 &result_param,
389 true,
390 render_view_id_,
391 page_url_);
392 NPObjectMsg_Invoke::WriteReplyParams(reply_msg, result_param, return_value);
393 channel_->Send(reply_msg);
394 }
395
OnEvaluate(const std::string & script,bool popups_allowed,IPC::Message * reply_msg)396 void NPObjectStub::OnEvaluate(const std::string& script,
397 bool popups_allowed,
398 IPC::Message* reply_msg) {
399 if (IsPluginProcess()) {
400 NOTREACHED() << "Should only be called on NPObjects in the renderer";
401 return;
402 }
403
404 NPVariant result_var;
405 NPString script_string;
406 script_string.UTF8Characters = script.c_str();
407 script_string.UTF8Length = static_cast<unsigned int>(script.length());
408
409 bool return_value = WebBindings::evaluateHelper(0, popups_allowed, npobject_,
410 &script_string, &result_var);
411
412 NPVariant_Param result_param;
413 CreateNPVariantParam(result_var,
414 channel_.get(),
415 &result_param,
416 true,
417 render_view_id_,
418 page_url_);
419 NPObjectMsg_Evaluate::WriteReplyParams(reply_msg, result_param, return_value);
420 channel_->Send(reply_msg);
421 }
422
423 } // namespace content
424