// 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