// 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_stub.h"
#include "content/child/npapi/np_channel_base.h"
#include "content/child/npapi/npobject_util.h"
#include "content/child/plugin_messages.h"
#include "content/public/common/content_client.h"
#include "content/public/common/content_switches.h"
#include "third_party/WebKit/public/web/WebBindings.h"
#include "third_party/npapi/bindings/npapi.h"
#include "third_party/npapi/bindings/npruntime.h"
#if defined(OS_WIN)
#include "base/command_line.h"
#include "content/common/plugin_constants_win.h"
#endif
using blink::WebBindings;
namespace content {
NPObjectStub::NPObjectStub(
NPObject* npobject,
NPChannelBase* channel,
int route_id,
int render_view_id,
const GURL& page_url)
: npobject_(npobject),
channel_(channel),
route_id_(route_id),
render_view_id_(render_view_id),
page_url_(page_url) {
channel_->AddMappingForNPObjectStub(route_id, npobject);
channel_->AddRoute(route_id, this, this);
// We retain the object just as PluginHost does if everything was in-process.
WebBindings::retainObject(npobject_);
}
NPObjectStub::~NPObjectStub() {
channel_->RemoveRoute(route_id_);
DCHECK(!npobject_);
}
void NPObjectStub::DeleteSoon() {
if (npobject_) {
channel_->RemoveMappingForNPObjectStub(route_id_, npobject_);
// We need to NULL npobject_ prior to calling releaseObject() to avoid
// problems with re-entrancy. See http://crbug.com/94179#c17 for more
// details on how this can happen.
NPObject* npobject = npobject_;
npobject_ = NULL;
WebBindings::releaseObject(npobject);
base::MessageLoop::current()->DeleteSoon(FROM_HERE, this);
}
}
bool NPObjectStub::Send(IPC::Message* msg) {
return channel_->Send(msg);
}
NPObject* NPObjectStub::GetUnderlyingNPObject() {
return npobject_;
}
IPC::Listener* NPObjectStub::GetChannelListener() {
return static_cast<IPC::Listener*>(this);
}
bool NPObjectStub::OnMessageReceived(const IPC::Message& msg) {
GetContentClient()->SetActiveURL(page_url_);
if (!npobject_) {
if (msg.is_sync()) {
// The object could be garbage because the frame has gone away, so
// just send an error reply to the caller.
IPC::Message* reply = IPC::SyncMessage::GenerateReply(&msg);
reply->set_reply_error();
Send(reply);
}
return true;
}
bool handled = true;
IPC_BEGIN_MESSAGE_MAP(NPObjectStub, msg)
IPC_MESSAGE_HANDLER_DELAY_REPLY(NPObjectMsg_Release, OnRelease);
IPC_MESSAGE_HANDLER(NPObjectMsg_HasMethod, OnHasMethod);
IPC_MESSAGE_HANDLER_DELAY_REPLY(NPObjectMsg_Invoke, OnInvoke);
IPC_MESSAGE_HANDLER(NPObjectMsg_HasProperty, OnHasProperty);
IPC_MESSAGE_HANDLER(NPObjectMsg_GetProperty, OnGetProperty);
IPC_MESSAGE_HANDLER_DELAY_REPLY(NPObjectMsg_SetProperty, OnSetProperty);
IPC_MESSAGE_HANDLER(NPObjectMsg_RemoveProperty, OnRemoveProperty);
IPC_MESSAGE_HANDLER(NPObjectMsg_Invalidate, OnInvalidate);
IPC_MESSAGE_HANDLER(NPObjectMsg_Enumeration, OnEnumeration);
IPC_MESSAGE_HANDLER_DELAY_REPLY(NPObjectMsg_Construct, OnConstruct);
IPC_MESSAGE_HANDLER_DELAY_REPLY(NPObjectMsg_Evaluate, OnEvaluate);
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
DCHECK(handled);
return handled;
}
void NPObjectStub::OnChannelError() {
DeleteSoon();
}
void NPObjectStub::OnRelease(IPC::Message* reply_msg) {
Send(reply_msg);
DeleteSoon();
}
void NPObjectStub::OnHasMethod(const NPIdentifier_Param& name,
bool* result) {
NPIdentifier id = CreateNPIdentifier(name);
// If we're in the plugin process, then the stub is holding onto an NPObject
// from the plugin, so all function calls on it need to go through the
// functions in NPClass. If we're in the renderer process, then we just call
// the NPN_ functions.
if (IsPluginProcess()) {
if (npobject_->_class->hasMethod) {
*result = npobject_->_class->hasMethod(npobject_, id);
} else {
*result = false;
}
} else {
*result = WebBindings::hasMethod(0, npobject_, id);
}
}
void NPObjectStub::OnInvoke(bool is_default,
const NPIdentifier_Param& method,
const std::vector<NPVariant_Param>& args,
IPC::Message* reply_msg) {
bool return_value = false;
NPVariant_Param result_param;
NPVariant result_var;
VOID_TO_NPVARIANT(result_var);
result_param.type = NPVARIANT_PARAM_VOID;
int arg_count = static_cast<int>(args.size());
NPVariant* args_var = new NPVariant[arg_count];
for (int i = 0; i < arg_count; ++i) {
if (!CreateNPVariant(args[i],
channel_.get(),
&(args_var[i]),
render_view_id_,
page_url_)) {
NPObjectMsg_Invoke::WriteReplyParams(
reply_msg, result_param, return_value);
channel_->Send(reply_msg);
delete[] args_var;
return;
}
}
if (is_default) {
if (IsPluginProcess()) {
if (npobject_->_class->invokeDefault) {
return_value = npobject_->_class->invokeDefault(
npobject_, args_var, arg_count, &result_var);
} else {
return_value = false;
}
} else {
return_value = WebBindings::invokeDefault(
0, npobject_, args_var, arg_count, &result_var);
}
} else {
NPIdentifier id = CreateNPIdentifier(method);
if (IsPluginProcess()) {
if (npobject_->_class->invoke) {
return_value = npobject_->_class->invoke(
npobject_, id, args_var, arg_count, &result_var);
} else {
return_value = false;
}
} else {
return_value = WebBindings::invoke(
0, npobject_, id, args_var, arg_count, &result_var);
}
}
for (int i = 0; i < arg_count; ++i)
WebBindings::releaseVariantValue(&(args_var[i]));
delete[] args_var;
CreateNPVariantParam(result_var,
channel_.get(),
&result_param,
true,
render_view_id_,
page_url_);
NPObjectMsg_Invoke::WriteReplyParams(reply_msg, result_param, return_value);
channel_->Send(reply_msg);
}
void NPObjectStub::OnHasProperty(const NPIdentifier_Param& name,
bool* result) {
NPIdentifier id = CreateNPIdentifier(name);
if (IsPluginProcess()) {
if (npobject_->_class->hasProperty) {
*result = npobject_->_class->hasProperty(npobject_, id);
} else {
*result = false;
}
} else {
*result = WebBindings::hasProperty(0, npobject_, id);
}
}
void NPObjectStub::OnGetProperty(const NPIdentifier_Param& name,
NPVariant_Param* property,
bool* result) {
NPVariant result_var;
VOID_TO_NPVARIANT(result_var);
NPIdentifier id = CreateNPIdentifier(name);
if (IsPluginProcess()) {
if (npobject_->_class->getProperty) {
*result = npobject_->_class->getProperty(npobject_, id, &result_var);
} else {
*result = false;
}
} else {
*result = WebBindings::getProperty(0, npobject_, id, &result_var);
}
CreateNPVariantParam(
result_var, channel_.get(), property, true, render_view_id_, page_url_);
}
void NPObjectStub::OnSetProperty(const NPIdentifier_Param& name,
const NPVariant_Param& property,
IPC::Message* reply_msg) {
bool result = false;
NPIdentifier id = CreateNPIdentifier(name);
NPVariant property_var;
if (!CreateNPVariant(property,
channel_.get(),
&property_var,
render_view_id_,
page_url_)) {
NPObjectMsg_SetProperty::WriteReplyParams(reply_msg, result);
channel_->Send(reply_msg);
return;
}
if (IsPluginProcess()) {
if (npobject_->_class->setProperty) {
#if defined(OS_WIN)
static base::FilePath plugin_path =
base::CommandLine::ForCurrentProcess()->GetSwitchValuePath(
switches::kPluginPath);
static std::wstring filename = base::StringToLowerASCII(
plugin_path.BaseName().value());
static NPIdentifier fullscreen =
WebBindings::getStringIdentifier("fullScreen");
if (filename == kNewWMPPlugin && id == fullscreen) {
// Workaround for bug 15985, which is if Flash causes WMP to go
// full screen a deadlock can occur when WMP calls SetFocus.
NPObjectMsg_SetProperty::WriteReplyParams(reply_msg, true);
Send(reply_msg);
reply_msg = NULL;
}
#endif
result = npobject_->_class->setProperty(npobject_, id, &property_var);
} else {
result = false;
}
} else {
result = WebBindings::setProperty(0, npobject_, id, &property_var);
}
WebBindings::releaseVariantValue(&property_var);
if (reply_msg) {
NPObjectMsg_SetProperty::WriteReplyParams(reply_msg, result);
Send(reply_msg);
}
}
void NPObjectStub::OnRemoveProperty(const NPIdentifier_Param& name,
bool* result) {
NPIdentifier id = CreateNPIdentifier(name);
if (IsPluginProcess()) {
if (npobject_->_class->removeProperty) {
*result = npobject_->_class->removeProperty(npobject_, id);
} else {
*result = false;
}
} else {
*result = WebBindings::removeProperty(0, npobject_, id);
}
}
void NPObjectStub::OnInvalidate() {
if (!IsPluginProcess()) {
NOTREACHED() << "Should only be called on NPObjects in the plugin";
return;
}
if (!npobject_->_class->invalidate)
return;
npobject_->_class->invalidate(npobject_);
}
void NPObjectStub::OnEnumeration(std::vector<NPIdentifier_Param>* value,
bool* result) {
NPIdentifier* value_np = NULL;
unsigned int count = 0;
if (!IsPluginProcess()) {
*result = WebBindings::enumerate(0, npobject_, &value_np, &count);
} else {
if (npobject_->_class->structVersion < NP_CLASS_STRUCT_VERSION_ENUM ||
!npobject_->_class->enumerate) {
*result = false;
return;
}
*result = npobject_->_class->enumerate(npobject_, &value_np, &count);
}
if (!*result)
return;
for (unsigned int i = 0; i < count; ++i) {
NPIdentifier_Param param;
CreateNPIdentifierParam(value_np[i], ¶m);
value->push_back(param);
}
free(value_np);
}
void NPObjectStub::OnConstruct(const std::vector<NPVariant_Param>& args,
IPC::Message* reply_msg) {
bool return_value = false;
NPVariant_Param result_param;
NPVariant result_var;
VOID_TO_NPVARIANT(result_var);
int arg_count = static_cast<int>(args.size());
NPVariant* args_var = new NPVariant[arg_count];
for (int i = 0; i < arg_count; ++i) {
if (!CreateNPVariant(args[i],
channel_.get(),
&(args_var[i]),
render_view_id_,
page_url_)) {
NPObjectMsg_Invoke::WriteReplyParams(
reply_msg, result_param, return_value);
channel_->Send(reply_msg);
delete[] args_var;
return;
}
}
if (IsPluginProcess()) {
if (npobject_->_class->structVersion >= NP_CLASS_STRUCT_VERSION_CTOR &&
npobject_->_class->construct) {
return_value = npobject_->_class->construct(
npobject_, args_var, arg_count, &result_var);
} else {
return_value = false;
}
} else {
return_value = WebBindings::construct(
0, npobject_, args_var, arg_count, &result_var);
}
for (int i = 0; i < arg_count; ++i)
WebBindings::releaseVariantValue(&(args_var[i]));
delete[] args_var;
CreateNPVariantParam(result_var,
channel_.get(),
&result_param,
true,
render_view_id_,
page_url_);
NPObjectMsg_Invoke::WriteReplyParams(reply_msg, result_param, return_value);
channel_->Send(reply_msg);
}
void NPObjectStub::OnEvaluate(const std::string& script,
bool popups_allowed,
IPC::Message* reply_msg) {
if (IsPluginProcess()) {
NOTREACHED() << "Should only be called on NPObjects in the renderer";
return;
}
NPVariant result_var;
NPString script_string;
script_string.UTF8Characters = script.c_str();
script_string.UTF8Length = static_cast<unsigned int>(script.length());
bool return_value = WebBindings::evaluateHelper(0, popups_allowed, npobject_,
&script_string, &result_var);
NPVariant_Param result_param;
CreateNPVariantParam(result_var,
channel_.get(),
&result_param,
true,
render_view_id_,
page_url_);
NPObjectMsg_Evaluate::WriteReplyParams(reply_msg, result_param, return_value);
channel_->Send(reply_msg);
}
} // namespace content