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