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_util.h"
6
7#include "base/strings/string_util.h"
8#include "content/child/npapi/np_channel_base.h"
9#include "content/child/npapi/npobject_proxy.h"
10#include "content/child/npapi/plugin_host.h"
11#include "content/child/plugin_messages.h"
12#include "third_party/WebKit/public/web/WebBindings.h"
13#include "third_party/npapi/bindings/nphostapi.h"
14
15using blink::WebBindings;
16
17namespace content {
18
19// true if the current process is a plugin process, false otherwise.
20static bool g_plugin_process;
21
22namespace {
23#if defined(ENABLE_PLUGINS)
24// The next 7 functions are called by the plugin code when it's using the
25// NPObject.  Plugins always ignore the functions in NPClass (except allocate
26// and deallocate), and instead just use the function pointers that were
27// passed in NPInitialize.
28// When the renderer interacts with an NPObject from the plugin, it of course
29// uses the function pointers in its NPClass structure.
30static bool NPN_HasMethodPatch(NPP npp,
31                               NPObject *npobj,
32                               NPIdentifier methodName) {
33  return NPObjectProxy::NPHasMethod(npobj, methodName);
34}
35
36static bool NPN_InvokePatch(NPP npp, NPObject *npobj,
37                            NPIdentifier methodName,
38                            const NPVariant *args,
39                            uint32_t argCount,
40                            NPVariant *result) {
41  return NPObjectProxy::NPInvokePrivate(npp, npobj, false, methodName, args,
42                                        argCount, result);
43}
44
45static bool NPN_InvokeDefaultPatch(NPP npp,
46                                   NPObject *npobj,
47                                   const NPVariant *args,
48                                   uint32_t argCount,
49                                   NPVariant *result) {
50  return NPObjectProxy::NPInvokePrivate(npp, npobj, true, 0, args, argCount,
51                                        result);
52}
53
54static bool NPN_HasPropertyPatch(NPP npp,
55                                 NPObject *npobj,
56                                 NPIdentifier propertyName) {
57  return NPObjectProxy::NPHasProperty(npobj, propertyName);
58}
59
60static bool NPN_GetPropertyPatch(NPP npp,
61                                 NPObject *npobj,
62                                 NPIdentifier propertyName,
63                                 NPVariant *result) {
64  return NPObjectProxy::NPGetProperty(npobj, propertyName, result);
65}
66
67static bool NPN_SetPropertyPatch(NPP npp,
68                                 NPObject *npobj,
69                                 NPIdentifier propertyName,
70                                 const NPVariant *value) {
71  return NPObjectProxy::NPSetProperty(npobj, propertyName, value);
72}
73
74static bool NPN_RemovePropertyPatch(NPP npp,
75                                    NPObject *npobj,
76                                    NPIdentifier propertyName) {
77  return NPObjectProxy::NPRemoveProperty(npobj, propertyName);
78}
79
80static bool NPN_EvaluatePatch(NPP npp,
81                              NPObject *npobj,
82                              NPString *script,
83                              NPVariant *result) {
84  return NPObjectProxy::NPNEvaluate(npp, npobj, script, result);
85}
86
87
88static void NPN_SetExceptionPatch(NPObject *obj, const NPUTF8 *message) {
89  std::string message_str(message);
90  if (IsPluginProcess()) {
91    NPChannelBase* renderer_channel = NPChannelBase::GetCurrentChannel();
92    if (renderer_channel)
93      renderer_channel->Send(new PluginHostMsg_SetException(message_str));
94  } else {
95    WebBindings::setException(obj, message_str.c_str());
96  }
97}
98
99static bool NPN_EnumeratePatch(NPP npp, NPObject *obj,
100                               NPIdentifier **identifier, uint32_t *count) {
101  return NPObjectProxy::NPNEnumerate(obj, identifier, count);
102}
103
104// The overrided table of functions provided to the plugin.
105NPNetscapeFuncs *GetHostFunctions() {
106  static bool init = false;
107  static NPNetscapeFuncs host_funcs;
108  if (init)
109    return &host_funcs;
110
111  memset(&host_funcs, 0, sizeof(host_funcs));
112  host_funcs.invoke = NPN_InvokePatch;
113  host_funcs.invokeDefault = NPN_InvokeDefaultPatch;
114  host_funcs.evaluate = NPN_EvaluatePatch;
115  host_funcs.getproperty = NPN_GetPropertyPatch;
116  host_funcs.setproperty = NPN_SetPropertyPatch;
117  host_funcs.removeproperty = NPN_RemovePropertyPatch;
118  host_funcs.hasproperty = NPN_HasPropertyPatch;
119  host_funcs.hasmethod = NPN_HasMethodPatch;
120  host_funcs.setexception = NPN_SetExceptionPatch;
121  host_funcs.enumerate = NPN_EnumeratePatch;
122
123  init = true;
124  return &host_funcs;
125}
126
127#endif  // defined(ENABLE_PLUGINS)
128}
129
130#if defined(ENABLE_PLUGINS)
131void PatchNPNFunctions() {
132  g_plugin_process = true;
133  NPNetscapeFuncs* funcs = GetHostFunctions();
134  PluginHost::Singleton()->PatchNPNetscapeFuncs(funcs);
135}
136#endif
137
138bool IsPluginProcess() {
139  return g_plugin_process;
140}
141
142void CreateNPIdentifierParam(NPIdentifier id, NPIdentifier_Param* param) {
143  param->identifier = id;
144}
145
146NPIdentifier CreateNPIdentifier(const NPIdentifier_Param& param) {
147  return param.identifier;
148}
149
150void CreateNPVariantParam(const NPVariant& variant,
151                          NPChannelBase* channel,
152                          NPVariant_Param* param,
153                          bool release,
154                          int render_view_id,
155                          const GURL& page_url) {
156  switch (variant.type) {
157    case NPVariantType_Void:
158      param->type = NPVARIANT_PARAM_VOID;
159      break;
160    case NPVariantType_Null:
161      param->type = NPVARIANT_PARAM_NULL;
162      break;
163    case NPVariantType_Bool:
164      param->type = NPVARIANT_PARAM_BOOL;
165      param->bool_value = variant.value.boolValue;
166      break;
167    case NPVariantType_Int32:
168      param->type = NPVARIANT_PARAM_INT;
169      param->int_value = variant.value.intValue;
170      break;
171    case NPVariantType_Double:
172      param->type = NPVARIANT_PARAM_DOUBLE;
173      param->double_value = variant.value.doubleValue;
174      break;
175    case NPVariantType_String:
176      param->type = NPVARIANT_PARAM_STRING;
177      if (variant.value.stringValue.UTF8Length) {
178        param->string_value.assign(variant.value.stringValue.UTF8Characters,
179                                   variant.value.stringValue.UTF8Length);
180      }
181      break;
182    case NPVariantType_Object: {
183      if (variant.value.objectValue->_class == NPObjectProxy::npclass()) {
184        param->type = NPVARIANT_PARAM_RECEIVER_OBJECT_ROUTING_ID;
185        NPObjectProxy* proxy =
186            NPObjectProxy::GetProxy(variant.value.objectValue);
187        DCHECK(proxy);
188        param->npobject_routing_id = proxy->route_id();
189        // Don't release, because our original variant is the same as our proxy.
190        release = false;
191      } else {
192        // The channel could be NULL if there was a channel error. The caller's
193        // Send call will fail anyways.
194        if (channel) {
195          // NPObjectStub adds its own reference to the NPObject it owns, so if
196          // we were supposed to release the corresponding variant
197          // (release==true), we should still do that.
198          param->type = NPVARIANT_PARAM_SENDER_OBJECT_ROUTING_ID;
199          int route_id = channel->GetExistingRouteForNPObjectStub(
200              variant.value.objectValue);
201          if (route_id != MSG_ROUTING_NONE) {
202            param->npobject_routing_id = route_id;
203          } else {
204            route_id = channel->GenerateRouteID();
205            new NPObjectStub(
206                variant.value.objectValue, channel, route_id, render_view_id,
207                page_url);
208            param->npobject_routing_id = route_id;
209          }
210
211          // Include the object's owner.
212          NPP owner = WebBindings::getObjectOwner(variant.value.objectValue);
213          param->npobject_owner_id =
214              channel->GetExistingRouteForNPObjectOwner(owner);
215        } else {
216          param->type = NPVARIANT_PARAM_VOID;
217        }
218      }
219      break;
220    }
221    default:
222      NOTREACHED();
223  }
224
225  if (release)
226    WebBindings::releaseVariantValue(const_cast<NPVariant*>(&variant));
227}
228
229bool CreateNPVariant(const NPVariant_Param& param,
230                     NPChannelBase* channel,
231                     NPVariant* result,
232                     int render_view_id,
233                     const GURL& page_url) {
234  switch (param.type) {
235    case NPVARIANT_PARAM_VOID:
236      result->type = NPVariantType_Void;
237      break;
238    case NPVARIANT_PARAM_NULL:
239      result->type = NPVariantType_Null;
240      break;
241    case NPVARIANT_PARAM_BOOL:
242      result->type = NPVariantType_Bool;
243      result->value.boolValue = param.bool_value;
244      break;
245    case NPVARIANT_PARAM_INT:
246      result->type = NPVariantType_Int32;
247      result->value.intValue = param.int_value;
248      break;
249    case NPVARIANT_PARAM_DOUBLE:
250      result->type = NPVariantType_Double;
251      result->value.doubleValue = param.double_value;
252      break;
253    case NPVARIANT_PARAM_STRING: {
254      result->type = NPVariantType_String;
255      void* buffer = malloc(param.string_value.size());
256      size_t size = param.string_value.size();
257      result->value.stringValue.UTF8Characters = static_cast<NPUTF8*>(buffer);
258      memcpy(buffer, param.string_value.c_str(), size);
259      result->value.stringValue.UTF8Length = static_cast<int>(size);
260      break;
261    }
262    case NPVARIANT_PARAM_SENDER_OBJECT_ROUTING_ID: {
263      result->type = NPVariantType_Object;
264      NPObject* object =
265          channel->GetExistingNPObjectProxy(param.npobject_routing_id);
266      if (object) {
267        WebBindings::retainObject(object);
268        result->value.objectValue = object;
269      } else {
270        NPP owner =
271            channel->GetExistingNPObjectOwner(param.npobject_owner_id);
272        // TODO(wez): Once NPObject tracking lands in Blink, check |owner| and
273        // return NPVariantType_Void if it is NULL.
274        result->value.objectValue =
275            NPObjectProxy::Create(channel,
276                                  param.npobject_routing_id,
277                                  render_view_id,
278                                  page_url,
279                                  owner);
280      }
281      break;
282    }
283    case NPVARIANT_PARAM_RECEIVER_OBJECT_ROUTING_ID: {
284      NPObjectBase* npobject_base =
285          channel->GetNPObjectListenerForRoute(param.npobject_routing_id);
286      if (!npobject_base) {
287        DLOG(WARNING) << "Invalid routing id passed in"
288                      << param.npobject_routing_id;
289        return false;
290      }
291
292      DCHECK(npobject_base->GetUnderlyingNPObject() != NULL);
293
294      result->type = NPVariantType_Object;
295      result->value.objectValue = npobject_base->GetUnderlyingNPObject();
296      WebBindings::retainObject(result->value.objectValue);
297      break;
298    }
299    default:
300      NOTREACHED();
301  }
302  return true;
303}
304
305}  // namespace content
306