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
21using blink::WebBindings;
22
23namespace content {
24
25NPObjectStub::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
43NPObjectStub::~NPObjectStub() {
44  channel_->RemoveRoute(route_id_);
45  DCHECK(!npobject_);
46}
47
48void 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
64bool NPObjectStub::Send(IPC::Message* msg) {
65  return channel_->Send(msg);
66}
67
68NPObject* NPObjectStub::GetUnderlyingNPObject() {
69  return npobject_;
70}
71
72IPC::Listener* NPObjectStub::GetChannelListener() {
73  return static_cast<IPC::Listener*>(this);
74}
75
76bool 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
109void NPObjectStub::OnChannelError() {
110  DeleteSoon();
111}
112
113void NPObjectStub::OnRelease(IPC::Message* reply_msg) {
114  Send(reply_msg);
115  DeleteSoon();
116}
117
118void 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
136void 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
205void 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
219void 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
240void 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
290void 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
304void 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
316void 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], &param);
338    value->push_back(param);
339  }
340
341  free(value_np);
342}
343
344void 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
396void 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