// Copyright 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "content/child/npapi/npobject_util.h" #include "base/strings/string_util.h" #include "content/child/npapi/np_channel_base.h" #include "content/child/npapi/npobject_proxy.h" #include "content/child/npapi/plugin_host.h" #include "content/child/plugin_messages.h" #include "third_party/WebKit/public/web/WebBindings.h" #include "third_party/npapi/bindings/nphostapi.h" using blink::WebBindings; namespace content { // true if the current process is a plugin process, false otherwise. static bool g_plugin_process; namespace { #if defined(ENABLE_PLUGINS) // The next 7 functions are called by the plugin code when it's using the // NPObject. Plugins always ignore the functions in NPClass (except allocate // and deallocate), and instead just use the function pointers that were // passed in NPInitialize. // When the renderer interacts with an NPObject from the plugin, it of course // uses the function pointers in its NPClass structure. static bool NPN_HasMethodPatch(NPP npp, NPObject *npobj, NPIdentifier methodName) { return NPObjectProxy::NPHasMethod(npobj, methodName); } static bool NPN_InvokePatch(NPP npp, NPObject *npobj, NPIdentifier methodName, const NPVariant *args, uint32_t argCount, NPVariant *result) { return NPObjectProxy::NPInvokePrivate(npp, npobj, false, methodName, args, argCount, result); } static bool NPN_InvokeDefaultPatch(NPP npp, NPObject *npobj, const NPVariant *args, uint32_t argCount, NPVariant *result) { return NPObjectProxy::NPInvokePrivate(npp, npobj, true, 0, args, argCount, result); } static bool NPN_HasPropertyPatch(NPP npp, NPObject *npobj, NPIdentifier propertyName) { return NPObjectProxy::NPHasProperty(npobj, propertyName); } static bool NPN_GetPropertyPatch(NPP npp, NPObject *npobj, NPIdentifier propertyName, NPVariant *result) { return NPObjectProxy::NPGetProperty(npobj, propertyName, result); } static bool NPN_SetPropertyPatch(NPP npp, NPObject *npobj, NPIdentifier propertyName, const NPVariant *value) { return NPObjectProxy::NPSetProperty(npobj, propertyName, value); } static bool NPN_RemovePropertyPatch(NPP npp, NPObject *npobj, NPIdentifier propertyName) { return NPObjectProxy::NPRemoveProperty(npobj, propertyName); } static bool NPN_EvaluatePatch(NPP npp, NPObject *npobj, NPString *script, NPVariant *result) { return NPObjectProxy::NPNEvaluate(npp, npobj, script, result); } static void NPN_SetExceptionPatch(NPObject *obj, const NPUTF8 *message) { std::string message_str(message); if (IsPluginProcess()) { NPChannelBase* renderer_channel = NPChannelBase::GetCurrentChannel(); if (renderer_channel) renderer_channel->Send(new PluginHostMsg_SetException(message_str)); } else { WebBindings::setException(obj, message_str.c_str()); } } static bool NPN_EnumeratePatch(NPP npp, NPObject *obj, NPIdentifier **identifier, uint32_t *count) { return NPObjectProxy::NPNEnumerate(obj, identifier, count); } // The overrided table of functions provided to the plugin. NPNetscapeFuncs *GetHostFunctions() { static bool init = false; static NPNetscapeFuncs host_funcs; if (init) return &host_funcs; memset(&host_funcs, 0, sizeof(host_funcs)); host_funcs.invoke = NPN_InvokePatch; host_funcs.invokeDefault = NPN_InvokeDefaultPatch; host_funcs.evaluate = NPN_EvaluatePatch; host_funcs.getproperty = NPN_GetPropertyPatch; host_funcs.setproperty = NPN_SetPropertyPatch; host_funcs.removeproperty = NPN_RemovePropertyPatch; host_funcs.hasproperty = NPN_HasPropertyPatch; host_funcs.hasmethod = NPN_HasMethodPatch; host_funcs.setexception = NPN_SetExceptionPatch; host_funcs.enumerate = NPN_EnumeratePatch; init = true; return &host_funcs; } #endif // defined(ENABLE_PLUGINS) } #if defined(ENABLE_PLUGINS) void PatchNPNFunctions() { g_plugin_process = true; NPNetscapeFuncs* funcs = GetHostFunctions(); PluginHost::Singleton()->PatchNPNetscapeFuncs(funcs); } #endif bool IsPluginProcess() { return g_plugin_process; } void CreateNPIdentifierParam(NPIdentifier id, NPIdentifier_Param* param) { param->identifier = id; } NPIdentifier CreateNPIdentifier(const NPIdentifier_Param& param) { return param.identifier; } void CreateNPVariantParam(const NPVariant& variant, NPChannelBase* channel, NPVariant_Param* param, bool release, int render_view_id, const GURL& page_url) { switch (variant.type) { case NPVariantType_Void: param->type = NPVARIANT_PARAM_VOID; break; case NPVariantType_Null: param->type = NPVARIANT_PARAM_NULL; break; case NPVariantType_Bool: param->type = NPVARIANT_PARAM_BOOL; param->bool_value = variant.value.boolValue; break; case NPVariantType_Int32: param->type = NPVARIANT_PARAM_INT; param->int_value = variant.value.intValue; break; case NPVariantType_Double: param->type = NPVARIANT_PARAM_DOUBLE; param->double_value = variant.value.doubleValue; break; case NPVariantType_String: param->type = NPVARIANT_PARAM_STRING; if (variant.value.stringValue.UTF8Length) { param->string_value.assign(variant.value.stringValue.UTF8Characters, variant.value.stringValue.UTF8Length); } break; case NPVariantType_Object: { if (variant.value.objectValue->_class == NPObjectProxy::npclass()) { param->type = NPVARIANT_PARAM_RECEIVER_OBJECT_ROUTING_ID; NPObjectProxy* proxy = NPObjectProxy::GetProxy(variant.value.objectValue); DCHECK(proxy); param->npobject_routing_id = proxy->route_id(); // Don't release, because our original variant is the same as our proxy. release = false; } else { // The channel could be NULL if there was a channel error. The caller's // Send call will fail anyways. if (channel) { // NPObjectStub adds its own reference to the NPObject it owns, so if // we were supposed to release the corresponding variant // (release==true), we should still do that. param->type = NPVARIANT_PARAM_SENDER_OBJECT_ROUTING_ID; int route_id = channel->GetExistingRouteForNPObjectStub( variant.value.objectValue); if (route_id != MSG_ROUTING_NONE) { param->npobject_routing_id = route_id; } else { route_id = channel->GenerateRouteID(); new NPObjectStub( variant.value.objectValue, channel, route_id, render_view_id, page_url); param->npobject_routing_id = route_id; } // Include the object's owner. NPP owner = WebBindings::getObjectOwner(variant.value.objectValue); param->npobject_owner_id = channel->GetExistingRouteForNPObjectOwner(owner); } else { param->type = NPVARIANT_PARAM_VOID; } } break; } default: NOTREACHED(); } if (release) WebBindings::releaseVariantValue(const_cast(&variant)); } bool CreateNPVariant(const NPVariant_Param& param, NPChannelBase* channel, NPVariant* result, int render_view_id, const GURL& page_url) { switch (param.type) { case NPVARIANT_PARAM_VOID: result->type = NPVariantType_Void; break; case NPVARIANT_PARAM_NULL: result->type = NPVariantType_Null; break; case NPVARIANT_PARAM_BOOL: result->type = NPVariantType_Bool; result->value.boolValue = param.bool_value; break; case NPVARIANT_PARAM_INT: result->type = NPVariantType_Int32; result->value.intValue = param.int_value; break; case NPVARIANT_PARAM_DOUBLE: result->type = NPVariantType_Double; result->value.doubleValue = param.double_value; break; case NPVARIANT_PARAM_STRING: { result->type = NPVariantType_String; void* buffer = malloc(param.string_value.size()); size_t size = param.string_value.size(); result->value.stringValue.UTF8Characters = static_cast(buffer); memcpy(buffer, param.string_value.c_str(), size); result->value.stringValue.UTF8Length = static_cast(size); break; } case NPVARIANT_PARAM_SENDER_OBJECT_ROUTING_ID: { result->type = NPVariantType_Object; NPObject* object = channel->GetExistingNPObjectProxy(param.npobject_routing_id); if (object) { WebBindings::retainObject(object); result->value.objectValue = object; } else { NPP owner = channel->GetExistingNPObjectOwner(param.npobject_owner_id); // TODO(wez): Once NPObject tracking lands in Blink, check |owner| and // return NPVariantType_Void if it is NULL. result->value.objectValue = NPObjectProxy::Create(channel, param.npobject_routing_id, render_view_id, page_url, owner); } break; } case NPVARIANT_PARAM_RECEIVER_OBJECT_ROUTING_ID: { NPObjectBase* npobject_base = channel->GetNPObjectListenerForRoute(param.npobject_routing_id); if (!npobject_base) { DLOG(WARNING) << "Invalid routing id passed in" << param.npobject_routing_id; return false; } DCHECK(npobject_base->GetUnderlyingNPObject() != NULL); result->type = NPVariantType_Object; result->value.objectValue = npobject_base->GetUnderlyingNPObject(); WebBindings::retainObject(result->value.objectValue); break; } default: NOTREACHED(); } return true; } } // namespace content