| // 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_proxy.h" |
| |
| #include "content/child/npapi/np_channel_base.h" |
| #include "content/child/npapi/npobject_util.h" |
| #include "content/child/plugin_messages.h" |
| #include "third_party/WebKit/public/web/WebBindings.h" |
| |
| #if defined(ENABLE_PLUGINS) |
| #include "content/child/npapi/plugin_instance.h" |
| #endif |
| |
| using blink::WebBindings; |
| |
| namespace content { |
| |
| struct NPObjectWrapper { |
| NPObject object; |
| NPObjectProxy* proxy; |
| }; |
| |
| NPClass NPObjectProxy::npclass_proxy_ = { |
| NP_CLASS_STRUCT_VERSION, |
| NPObjectProxy::NPAllocate, |
| NPObjectProxy::NPDeallocate, |
| NPObjectProxy::NPPInvalidate, |
| NPObjectProxy::NPHasMethod, |
| NPObjectProxy::NPInvoke, |
| NPObjectProxy::NPInvokeDefault, |
| NPObjectProxy::NPHasProperty, |
| NPObjectProxy::NPGetProperty, |
| NPObjectProxy::NPSetProperty, |
| NPObjectProxy::NPRemoveProperty, |
| NPObjectProxy::NPNEnumerate, |
| NPObjectProxy::NPNConstruct |
| }; |
| |
| NPObjectProxy* NPObjectProxy::GetProxy(NPObject* object) { |
| NPObjectProxy* proxy = NULL; |
| |
| // Wrapper exists only for NPObjects that we had created. |
| if (&npclass_proxy_ == object->_class) { |
| NPObjectWrapper* wrapper = reinterpret_cast<NPObjectWrapper*>(object); |
| proxy = wrapper->proxy; |
| } |
| |
| return proxy; |
| } |
| |
| NPObject* NPObjectProxy::GetUnderlyingNPObject() { |
| return NULL; |
| } |
| |
| IPC::Listener* NPObjectProxy::GetChannelListener() { |
| return static_cast<IPC::Listener*>(this); |
| } |
| |
| NPObjectProxy::NPObjectProxy( |
| NPChannelBase* channel, |
| int route_id, |
| int render_view_id, |
| const GURL& page_url) |
| : channel_(channel), |
| route_id_(route_id), |
| render_view_id_(render_view_id), |
| page_url_(page_url) { |
| channel_->AddRoute(route_id, this, this); |
| } |
| |
| NPObjectProxy::~NPObjectProxy() { |
| if (channel_.get()) { |
| // This NPObjectProxy instance is now invalid and should not be reused for |
| // requests initiated by plugins. We may receive requests for the |
| // same NPObject in the context of the outgoing NPObjectMsg_Release call. |
| // We should be creating new NPObjectProxy instances to wrap these |
| // NPObjects. |
| channel_->RemoveMappingForNPObjectProxy(route_id_); |
| channel_->RemoveRoute(route_id_); |
| Send(new NPObjectMsg_Release(route_id_)); |
| } |
| } |
| |
| NPObject* NPObjectProxy::Create(NPChannelBase* channel, |
| int route_id, |
| int render_view_id, |
| const GURL& page_url, |
| NPP owner) { |
| NPObjectWrapper* obj = reinterpret_cast<NPObjectWrapper*>( |
| WebBindings::createObject(owner, &npclass_proxy_)); |
| obj->proxy = new NPObjectProxy(channel, route_id, render_view_id, page_url); |
| channel->AddMappingForNPObjectProxy(route_id, &obj->object); |
| return reinterpret_cast<NPObject*>(obj); |
| } |
| |
| bool NPObjectProxy::Send(IPC::Message* msg) { |
| if (channel_.get()) |
| return channel_->Send(msg); |
| |
| delete msg; |
| return false; |
| } |
| |
| NPObject* NPObjectProxy::NPAllocate(NPP, NPClass*) { |
| return reinterpret_cast<NPObject*>(new NPObjectWrapper); |
| } |
| |
| void NPObjectProxy::NPDeallocate(NPObject* npObj) { |
| NPObjectWrapper* obj = reinterpret_cast<NPObjectWrapper*>(npObj); |
| delete obj->proxy; |
| delete obj; |
| } |
| |
| bool NPObjectProxy::OnMessageReceived(const IPC::Message& msg) { |
| NOTREACHED(); |
| return false; |
| } |
| |
| void NPObjectProxy::OnChannelError() { |
| // Release our ref count of the plugin channel object, as it addrefs the |
| // process. |
| channel_ = NULL; |
| } |
| |
| bool NPObjectProxy::NPHasMethod(NPObject *obj, |
| NPIdentifier name) { |
| if (obj == NULL) |
| return false; |
| |
| bool result = false; |
| NPObjectProxy* proxy = GetProxy(obj); |
| |
| if (!proxy) { |
| return obj->_class->hasMethod(obj, name); |
| } |
| |
| NPIdentifier_Param name_param; |
| CreateNPIdentifierParam(name, &name_param); |
| |
| proxy->Send(new NPObjectMsg_HasMethod(proxy->route_id(), name_param, |
| &result)); |
| return result; |
| } |
| |
| bool NPObjectProxy::NPInvoke(NPObject *obj, |
| NPIdentifier name, |
| const NPVariant *args, |
| uint32_t arg_count, |
| NPVariant *result) { |
| return NPInvokePrivate(0, obj, false, name, args, arg_count, result); |
| } |
| |
| bool NPObjectProxy::NPInvokeDefault(NPObject *npobj, |
| const NPVariant *args, |
| uint32_t arg_count, |
| NPVariant *result) { |
| return NPInvokePrivate(0, npobj, true, 0, args, arg_count, result); |
| } |
| |
| bool NPObjectProxy::NPInvokePrivate(NPP npp, |
| NPObject *obj, |
| bool is_default, |
| NPIdentifier name, |
| const NPVariant *args, |
| uint32_t arg_count, |
| NPVariant *np_result) { |
| if (obj == NULL) |
| return false; |
| |
| NPObjectProxy* proxy = GetProxy(obj); |
| if (!proxy) { |
| if (is_default) { |
| return obj->_class->invokeDefault(obj, args, arg_count, np_result); |
| } else { |
| return obj->_class->invoke(obj, name, args, arg_count, np_result); |
| } |
| } |
| |
| bool result = false; |
| int render_view_id = proxy->render_view_id_; |
| NPIdentifier_Param name_param; |
| if (is_default) { |
| // The data won't actually get used, but set it so we don't send random |
| // data. |
| name_param.identifier = NULL; |
| } else { |
| CreateNPIdentifierParam(name, &name_param); |
| } |
| |
| // Note: This instance can get destroyed in the context of |
| // Send so addref the channel in this scope. |
| scoped_refptr<NPChannelBase> channel_copy = proxy->channel_; |
| std::vector<NPVariant_Param> args_param; |
| for (unsigned int i = 0; i < arg_count; ++i) { |
| NPVariant_Param param; |
| CreateNPVariantParam(args[i], |
| channel_copy.get(), |
| ¶m, |
| false, |
| render_view_id, |
| proxy->page_url_); |
| args_param.push_back(param); |
| } |
| |
| NPVariant_Param param_result; |
| NPObjectMsg_Invoke* msg = new NPObjectMsg_Invoke( |
| proxy->route_id_, is_default, name_param, args_param, ¶m_result, |
| &result); |
| |
| // If we're in the plugin process and this invoke leads to a dialog box, the |
| // plugin will hang the window hierarchy unless we pump the window message |
| // queue while waiting for a reply. We need to do this to simulate what |
| // happens when everything runs in-process (while calling MessageBox window |
| // messages are pumped). |
| if (IsPluginProcess() && proxy->channel()) { |
| msg->set_pump_messages_event( |
| proxy->channel()->GetModalDialogEvent(render_view_id)); |
| } |
| |
| GURL page_url = proxy->page_url_; |
| proxy->Send(msg); |
| |
| // Send may delete proxy. |
| proxy = NULL; |
| |
| if (!result) |
| return false; |
| |
| CreateNPVariant( |
| param_result, channel_copy.get(), np_result, render_view_id, page_url); |
| return true; |
| } |
| |
| bool NPObjectProxy::NPHasProperty(NPObject *obj, |
| NPIdentifier name) { |
| if (obj == NULL) |
| return false; |
| |
| bool result = false; |
| NPObjectProxy* proxy = GetProxy(obj); |
| if (!proxy) { |
| return obj->_class->hasProperty(obj, name); |
| } |
| |
| NPIdentifier_Param name_param; |
| CreateNPIdentifierParam(name, &name_param); |
| |
| NPVariant_Param param; |
| proxy->Send(new NPObjectMsg_HasProperty( |
| proxy->route_id(), name_param, &result)); |
| |
| // Send may delete proxy. |
| proxy = NULL; |
| |
| return result; |
| } |
| |
| bool NPObjectProxy::NPGetProperty(NPObject *obj, |
| NPIdentifier name, |
| NPVariant *np_result) { |
| // Please refer to http://code.google.com/p/chromium/issues/detail?id=2556, |
| // which was a crash in the XStandard plugin during plugin shutdown. The |
| // crash occured because the plugin requests the plugin script object, |
| // which fails. The plugin does not check the result of the operation and |
| // invokes NPN_GetProperty on a NULL object which lead to the crash. If |
| // we observe similar crashes in other methods in the future, these null |
| // checks may have to be replicated in the other methods in this class. |
| if (obj == NULL) |
| return false; |
| |
| NPObjectProxy* proxy = GetProxy(obj); |
| if (!proxy) { |
| return obj->_class->getProperty(obj, name, np_result); |
| } |
| |
| bool result = false; |
| int render_view_id = proxy->render_view_id_; |
| NPIdentifier_Param name_param; |
| CreateNPIdentifierParam(name, &name_param); |
| |
| NPVariant_Param param; |
| scoped_refptr<NPChannelBase> channel(proxy->channel_); |
| |
| GURL page_url = proxy->page_url_; |
| proxy->Send(new NPObjectMsg_GetProperty( |
| proxy->route_id(), name_param, ¶m, &result)); |
| // Send may delete proxy. |
| proxy = NULL; |
| if (!result) |
| return false; |
| |
| CreateNPVariant( |
| param, channel.get(), np_result, render_view_id, page_url); |
| |
| return true; |
| } |
| |
| bool NPObjectProxy::NPSetProperty(NPObject *obj, |
| NPIdentifier name, |
| const NPVariant *value) { |
| if (obj == NULL) |
| return false; |
| |
| NPObjectProxy* proxy = GetProxy(obj); |
| if (!proxy) { |
| return obj->_class->setProperty(obj, name, value); |
| } |
| |
| bool result = false; |
| int render_view_id = proxy->render_view_id_; |
| NPIdentifier_Param name_param; |
| CreateNPIdentifierParam(name, &name_param); |
| |
| NPVariant_Param value_param; |
| CreateNPVariantParam( |
| *value, proxy->channel(), &value_param, false, render_view_id, |
| proxy->page_url_); |
| |
| proxy->Send(new NPObjectMsg_SetProperty( |
| proxy->route_id(), name_param, value_param, &result)); |
| // Send may delete proxy. |
| proxy = NULL; |
| |
| return result; |
| } |
| |
| bool NPObjectProxy::NPRemoveProperty(NPObject *obj, |
| NPIdentifier name) { |
| if (obj == NULL) |
| return false; |
| |
| bool result = false; |
| NPObjectProxy* proxy = GetProxy(obj); |
| if (!proxy) { |
| return obj->_class->removeProperty(obj, name); |
| } |
| |
| NPIdentifier_Param name_param; |
| CreateNPIdentifierParam(name, &name_param); |
| |
| NPVariant_Param param; |
| proxy->Send(new NPObjectMsg_RemoveProperty( |
| proxy->route_id(), name_param, &result)); |
| // Send may delete proxy. |
| proxy = NULL; |
| |
| return result; |
| } |
| |
| void NPObjectProxy::NPPInvalidate(NPObject *obj) { |
| if (obj == NULL) |
| return; |
| |
| NPObjectProxy* proxy = GetProxy(obj); |
| if (!proxy) { |
| obj->_class->invalidate(obj); |
| return; |
| } |
| |
| proxy->Send(new NPObjectMsg_Invalidate(proxy->route_id())); |
| // Send may delete proxy. |
| proxy = NULL; |
| } |
| |
| bool NPObjectProxy::NPNEnumerate(NPObject *obj, |
| NPIdentifier **value, |
| uint32_t *count) { |
| if (obj == NULL) |
| return false; |
| |
| bool result = false; |
| NPObjectProxy* proxy = GetProxy(obj); |
| if (!proxy) { |
| if (obj->_class->structVersion >= NP_CLASS_STRUCT_VERSION_ENUM) { |
| return obj->_class->enumerate(obj, value, count); |
| } else { |
| return false; |
| } |
| } |
| |
| std::vector<NPIdentifier_Param> value_param; |
| proxy->Send(new NPObjectMsg_Enumeration( |
| proxy->route_id(), &value_param, &result)); |
| // Send may delete proxy. |
| proxy = NULL; |
| |
| if (!result) |
| return false; |
| |
| *count = static_cast<unsigned int>(value_param.size()); |
| *value = static_cast<NPIdentifier *>(malloc(sizeof(NPIdentifier) * *count)); |
| for (unsigned int i = 0; i < *count; ++i) |
| (*value)[i] = CreateNPIdentifier(value_param[i]); |
| |
| return true; |
| } |
| |
| bool NPObjectProxy::NPNConstruct(NPObject *obj, |
| const NPVariant *args, |
| uint32_t arg_count, |
| NPVariant *np_result) { |
| if (obj == NULL) |
| return false; |
| |
| NPObjectProxy* proxy = GetProxy(obj); |
| if (!proxy) { |
| if (obj->_class->structVersion >= NP_CLASS_STRUCT_VERSION_CTOR) { |
| return obj->_class->construct(obj, args, arg_count, np_result); |
| } else { |
| return false; |
| } |
| } |
| |
| bool result = false; |
| int render_view_id = proxy->render_view_id_; |
| |
| // Note: This instance can get destroyed in the context of |
| // Send so addref the channel in this scope. |
| scoped_refptr<NPChannelBase> channel_copy = proxy->channel_; |
| std::vector<NPVariant_Param> args_param; |
| for (unsigned int i = 0; i < arg_count; ++i) { |
| NPVariant_Param param; |
| CreateNPVariantParam(args[i], |
| channel_copy.get(), |
| ¶m, |
| false, |
| render_view_id, |
| proxy->page_url_); |
| args_param.push_back(param); |
| } |
| |
| NPVariant_Param param_result; |
| NPObjectMsg_Construct* msg = new NPObjectMsg_Construct( |
| proxy->route_id_, args_param, ¶m_result, &result); |
| |
| // See comment in NPObjectProxy::NPInvokePrivate. |
| if (IsPluginProcess() && proxy->channel()) { |
| msg->set_pump_messages_event( |
| proxy->channel()->GetModalDialogEvent(proxy->render_view_id_)); |
| } |
| |
| GURL page_url = proxy->page_url_; |
| proxy->Send(msg); |
| |
| // Send may delete proxy. |
| proxy = NULL; |
| |
| if (!result) |
| return false; |
| |
| CreateNPVariant( |
| param_result, channel_copy.get(), np_result, render_view_id, page_url); |
| return true; |
| } |
| |
| bool NPObjectProxy::NPNEvaluate(NPP npp, |
| NPObject *obj, |
| NPString *script, |
| NPVariant *result_var) { |
| NPObjectProxy* proxy = GetProxy(obj); |
| if (!proxy) { |
| return false; |
| } |
| |
| bool result = false; |
| int render_view_id = proxy->render_view_id_; |
| bool popups_allowed = false; |
| |
| #if defined(ENABLE_PLUGINS) |
| if (npp) { |
| PluginInstance* plugin_instance = |
| reinterpret_cast<PluginInstance*>(npp->ndata); |
| if (plugin_instance) |
| popups_allowed = plugin_instance->popups_allowed(); |
| } |
| #endif |
| |
| NPVariant_Param result_param; |
| std::string script_str = std::string( |
| script->UTF8Characters, script->UTF8Length); |
| |
| NPObjectMsg_Evaluate* msg = new NPObjectMsg_Evaluate(proxy->route_id(), |
| script_str, |
| popups_allowed, |
| &result_param, |
| &result); |
| |
| // See comment in NPObjectProxy::NPInvokePrivate. |
| if (IsPluginProcess() && proxy->channel()) { |
| msg->set_pump_messages_event( |
| proxy->channel()->GetModalDialogEvent(render_view_id)); |
| } |
| scoped_refptr<NPChannelBase> channel(proxy->channel_); |
| |
| GURL page_url = proxy->page_url_; |
| proxy->Send(msg); |
| // Send may delete proxy. |
| proxy = NULL; |
| if (!result) |
| return false; |
| |
| CreateNPVariant( |
| result_param, channel.get(), result_var, render_view_id, page_url); |
| return true; |
| } |
| |
| } // namespace content |