summaryrefslogtreecommitdiffstats
path: root/content/plugin
diff options
context:
space:
mode:
authorjam@chromium.org <jam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-03-16 17:23:58 +0000
committerjam@chromium.org <jam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-03-16 17:23:58 +0000
commit3c5c6d8d4dd6b6b6fd3115dbbe2b155b9eb207f9 (patch)
tree6832386fa85e7dc6db0e094041b5bd3587dd7748 /content/plugin
parent0590a140d2291f2aebfb54179f1282d798faa6a8 (diff)
downloadchromium_src-3c5c6d8d4dd6b6b6fd3115dbbe2b155b9eb207f9.zip
chromium_src-3c5c6d8d4dd6b6b6fd3115dbbe2b155b9eb207f9.tar.gz
chromium_src-3c5c6d8d4dd6b6b6fd3115dbbe2b155b9eb207f9.tar.bz2
Move plugin code to content.
TBR=avi Review URL: http://codereview.chromium.org/6672048 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@78386 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'content/plugin')
-rw-r--r--content/plugin/DEPS8
-rw-r--r--content/plugin/npobject_base.h27
-rw-r--r--content/plugin/npobject_proxy.cc500
-rw-r--r--content/plugin/npobject_proxy.h123
-rw-r--r--content/plugin/npobject_stub.cc402
-rw-r--r--content/plugin/npobject_stub.h95
-rw-r--r--content/plugin/npobject_util.cc273
-rw-r--r--content/plugin/npobject_util.h73
-rw-r--r--content/plugin/plugin_channel.cc328
-rw-r--r--content/plugin/plugin_channel.h99
-rw-r--r--content/plugin/plugin_channel_base.cc241
-rw-r--r--content/plugin/plugin_channel_base.h149
-rw-r--r--content/plugin/plugin_interpose_util_mac.h57
-rw-r--r--content/plugin/plugin_interpose_util_mac.mm306
-rw-r--r--content/plugin/plugin_main.cc171
-rw-r--r--content/plugin/plugin_main_linux.cc72
-rw-r--r--content/plugin/plugin_main_mac.mm50
-rw-r--r--content/plugin/plugin_thread.cc193
-rw-r--r--content/plugin/plugin_thread.h54
-rw-r--r--content/plugin/webplugin_accelerated_surface_proxy_mac.cc79
-rw-r--r--content/plugin/webplugin_accelerated_surface_proxy_mac.h39
-rw-r--r--content/plugin/webplugin_delegate_stub.cc451
-rw-r--r--content/plugin/webplugin_delegate_stub.h135
-rw-r--r--content/plugin/webplugin_proxy.cc656
-rw-r--r--content/plugin/webplugin_proxy.h225
25 files changed, 4806 insertions, 0 deletions
diff --git a/content/plugin/DEPS b/content/plugin/DEPS
new file mode 100644
index 0000000..5868246
--- /dev/null
+++ b/content/plugin/DEPS
@@ -0,0 +1,8 @@
+include_rules = [
+ "+sandbox/src",
+ "+skia/ext",
+ "+third_party/npapi",
+ "+webkit/glue",
+ "+webkit/plugins",
+]
+
diff --git a/content/plugin/npobject_base.h b/content/plugin/npobject_base.h
new file mode 100644
index 0000000..fc6117a
--- /dev/null
+++ b/content/plugin/npobject_base.h
@@ -0,0 +1,27 @@
+// Copyright (c) 2006-2010 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.
+//
+// Base interface implemented by NPObjectProxy and NPObjectStub
+
+#ifndef CONTENT_PLUGIN_NPOBJECT_BASE_H_
+#define CONTENT_PLUGIN_NPOBJECT_BASE_H_
+#pragma once
+
+#include "ipc/ipc_channel.h"
+#include "third_party/npapi/bindings/npruntime.h"
+
+struct NPObject;
+
+class NPObjectBase {
+ public:
+ virtual ~NPObjectBase() {}
+
+ // Returns the underlying NPObject handled by this NPObjectBase instance.
+ virtual NPObject* GetUnderlyingNPObject() = 0;
+
+ // Returns the channel listener for this NPObjectBase instance.
+ virtual IPC::Channel::Listener* GetChannelListener() = 0;
+};
+
+#endif // CONTENT_PLUGIN_NPOBJECT_BASE_H_
diff --git a/content/plugin/npobject_proxy.cc b/content/plugin/npobject_proxy.cc
new file mode 100644
index 0000000..5c08724
--- /dev/null
+++ b/content/plugin/npobject_proxy.cc
@@ -0,0 +1,500 @@
+// Copyright (c) 2006-2008 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/plugin/npobject_proxy.h"
+
+#include "content/common/plugin_messages.h"
+#include "content/plugin/npobject_util.h"
+#include "content/plugin/plugin_channel.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebBindings.h"
+#include "webkit/glue/webkit_glue.h"
+#include "webkit/plugins/npapi/plugin_instance.h"
+
+using WebKit::WebBindings;
+
+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::Channel::Listener* NPObjectProxy::GetChannelListener() {
+ return static_cast<IPC::Channel::Listener*>(this);
+}
+
+NPObjectProxy::NPObjectProxy(
+ PluginChannelBase* channel,
+ int route_id,
+ gfx::NativeViewId containing_window,
+ const GURL& page_url)
+ : channel_(channel),
+ route_id_(route_id),
+ containing_window_(containing_window),
+ page_url_(page_url) {
+ channel_->AddRoute(route_id, this, this);
+}
+
+NPObjectProxy::~NPObjectProxy() {
+ if (channel_.get()) {
+ Send(new NPObjectMsg_Release(route_id_));
+ if (channel_.get())
+ channel_->RemoveRoute(route_id_);
+ }
+}
+
+NPObject* NPObjectProxy::Create(PluginChannelBase* channel,
+ int route_id,
+ gfx::NativeViewId containing_window,
+ const GURL& page_url) {
+ NPObjectWrapper* obj = reinterpret_cast<NPObjectWrapper*>(
+ WebBindings::createObject(0, &npclass_proxy_));
+ obj->proxy = new NPObjectProxy(
+ channel, route_id, containing_window, page_url);
+
+ 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;
+ gfx::NativeViewId containing_window = proxy->containing_window_;
+ 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<PluginChannelBase> 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, &param, false, containing_window,
+ 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, &param_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()) {
+ PluginChannel* channel = static_cast<PluginChannel*>(proxy->channel_.get());
+ if (channel) {
+ msg->set_pump_messages_event(
+ channel->GetModalDialogEvent(containing_window));
+ }
+ }
+
+ GURL page_url = proxy->page_url_;
+ proxy->Send(msg);
+
+ // Send may delete proxy.
+ proxy = NULL;
+
+ if (!result)
+ return false;
+
+ CreateNPVariant(
+ param_result, channel_copy, np_result, containing_window, 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;
+ gfx::NativeViewId containing_window = proxy->containing_window_;
+ NPIdentifier_Param name_param;
+ CreateNPIdentifierParam(name, &name_param);
+
+ NPVariant_Param param;
+ scoped_refptr<PluginChannelBase> channel(proxy->channel_);
+
+ GURL page_url = proxy->page_url_;
+ proxy->Send(new NPObjectMsg_GetProperty(
+ proxy->route_id(), name_param, &param, &result));
+ // Send may delete proxy.
+ proxy = NULL;
+ if (!result)
+ return false;
+
+ CreateNPVariant(
+ param, channel.get(), np_result, containing_window, 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;
+ gfx::NativeViewId containing_window = proxy->containing_window_;
+ NPIdentifier_Param name_param;
+ CreateNPIdentifierParam(name, &name_param);
+
+ NPVariant_Param value_param;
+ CreateNPVariantParam(
+ *value, proxy->channel(), &value_param, false,
+ containing_window, 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 *>(
+ NPN_MemAlloc(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;
+ gfx::NativeViewId containing_window = proxy->containing_window_;
+
+ // Note: This instance can get destroyed in the context of
+ // Send so addref the channel in this scope.
+ scoped_refptr<PluginChannelBase> 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, &param, false, containing_window,
+ proxy->page_url_);
+ args_param.push_back(param);
+ }
+
+ NPVariant_Param param_result;
+ NPObjectMsg_Construct* msg = new NPObjectMsg_Construct(
+ proxy->route_id_, args_param, &param_result, &result);
+
+ // See comment in NPObjectProxy::NPInvokePrivate.
+ if (IsPluginProcess()) {
+ PluginChannel* channel = static_cast<PluginChannel*>(proxy->channel_.get());
+ if (channel) {
+ msg->set_pump_messages_event(
+ channel->GetModalDialogEvent(proxy->containing_window_));
+ }
+ }
+
+ GURL page_url = proxy->page_url_;
+ proxy->Send(msg);
+
+ // Send may delete proxy.
+ proxy = NULL;
+
+ if (!result)
+ return false;
+
+ CreateNPVariant(
+ param_result, channel_copy, np_result, containing_window, 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;
+ gfx::NativeViewId containing_window = proxy->containing_window_;
+ bool popups_allowed = false;
+
+ if (npp) {
+ webkit::npapi::PluginInstance* plugin_instance =
+ reinterpret_cast<webkit::npapi::PluginInstance*>(npp->ndata);
+ if (plugin_instance)
+ popups_allowed = plugin_instance->popups_allowed();
+ }
+
+ 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()) {
+ PluginChannel* channel = static_cast<PluginChannel*>(proxy->channel_.get());
+ if (channel) {
+ msg->set_pump_messages_event(
+ channel->GetModalDialogEvent(proxy->containing_window_));
+ }
+ }
+ scoped_refptr<PluginChannelBase> 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, containing_window, page_url);
+ return true;
+}
diff --git a/content/plugin/npobject_proxy.h b/content/plugin/npobject_proxy.h
new file mode 100644
index 0000000..39996de
--- /dev/null
+++ b/content/plugin/npobject_proxy.h
@@ -0,0 +1,123 @@
+// Copyright (c) 2006-2008 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.
+//
+// A proxy for NPObject that sends all calls to the object to an NPObjectStub
+// running in a different process.
+
+#ifndef CONTENT_PLUGIN_NPOBJECT_PROXY_H_
+#define CONTENT_PLUGIN_NPOBJECT_PROXY_H_
+#pragma once
+
+#include "base/ref_counted.h"
+#include "content/plugin/npobject_base.h"
+#include "googleurl/src/gurl.h"
+#include "ipc/ipc_channel.h"
+#include "third_party/npapi/bindings/npruntime.h"
+#include "ui/gfx/native_widget_types.h"
+
+class PluginChannelBase;
+struct NPObject;
+
+// When running a plugin in a different process from the renderer, we need to
+// proxy calls to NPObjects across process boundaries. This happens both ways,
+// as a plugin can get an NPObject for the window, and a page can get an
+// NPObject for the plugin. In the process that interacts with the NPobject we
+// give it an NPObjectProxy instead. All calls to it are sent across an IPC
+// channel (specifically, a PluginChannelBase). The NPObjectStub on the other
+// side translates the IPC messages into calls to the actual NPObject, and
+// returns the marshalled result.
+class NPObjectProxy : public IPC::Channel::Listener,
+ public IPC::Message::Sender,
+ public NPObjectBase {
+ public:
+ ~NPObjectProxy();
+
+ static NPObject* Create(PluginChannelBase* channel,
+ int route_id,
+ gfx::NativeViewId containing_window,
+ const GURL& page_url);
+
+ // IPC::Message::Sender implementation:
+ virtual bool Send(IPC::Message* msg);
+ int route_id() { return route_id_; }
+ PluginChannelBase* channel() { return channel_; }
+
+ // The next 9 functions are called on NPObjects from the plugin and browser.
+ static bool NPHasMethod(NPObject *obj,
+ NPIdentifier name);
+ static bool NPInvoke(NPObject *obj,
+ NPIdentifier name,
+ const NPVariant *args,
+ uint32_t arg_count,
+ NPVariant *result);
+ static bool NPInvokeDefault(NPObject *npobj,
+ const NPVariant *args,
+ uint32_t arg_count,
+ NPVariant *result);
+ static bool NPHasProperty(NPObject *obj,
+ NPIdentifier name);
+ static bool NPGetProperty(NPObject *obj,
+ NPIdentifier name,
+ NPVariant *result);
+ static bool NPSetProperty(NPObject *obj,
+ NPIdentifier name,
+ const NPVariant *value);
+ static bool NPRemoveProperty(NPObject *obj,
+ NPIdentifier name);
+ static bool NPNEnumerate(NPObject *obj,
+ NPIdentifier **value,
+ uint32_t *count);
+ static bool NPNConstruct(NPObject *npobj,
+ const NPVariant *args,
+ uint32_t arg_count,
+ NPVariant *result);
+
+ // The next two functions are only called on NPObjects from the browser.
+ static bool NPNEvaluate(NPP npp,
+ NPObject *obj,
+ NPString *script,
+ NPVariant *result);
+
+ static bool NPInvokePrivate(NPP npp,
+ NPObject *obj,
+ bool is_default,
+ NPIdentifier name,
+ const NPVariant *args,
+ uint32_t arg_count,
+ NPVariant *result);
+
+ static NPObjectProxy* GetProxy(NPObject* object);
+ static const NPClass* npclass() { return &npclass_proxy_; }
+
+ // NPObjectBase implementation.
+ virtual NPObject* GetUnderlyingNPObject();
+
+ virtual IPC::Channel::Listener* GetChannelListener();
+
+ private:
+ NPObjectProxy(PluginChannelBase* channel,
+ int route_id,
+ gfx::NativeViewId containing_window,
+ const GURL& page_url);
+
+ // IPC::Channel::Listener implementation:
+ virtual bool OnMessageReceived(const IPC::Message& msg);
+ virtual void OnChannelError();
+
+ static NPObject* NPAllocate(NPP, NPClass*);
+ static void NPDeallocate(NPObject* npObj);
+
+ // This function is only caled on NPObjects from the plugin.
+ static void NPPInvalidate(NPObject *obj);
+ static NPClass npclass_proxy_;
+
+ scoped_refptr<PluginChannelBase> channel_;
+ int route_id_;
+ gfx::NativeViewId containing_window_;
+
+ // The url of the main frame hosting the plugin.
+ GURL page_url_;
+};
+
+#endif // CONTENT_PLUGIN_NPOBJECT_PROXY_H_
diff --git a/content/plugin/npobject_stub.cc b/content/plugin/npobject_stub.cc
new file mode 100644
index 0000000..e7402a8
--- /dev/null
+++ b/content/plugin/npobject_stub.cc
@@ -0,0 +1,402 @@
+// Copyright (c) 2011 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/plugin/npobject_stub.h"
+
+#include "content/common/content_client.h"
+#include "content/common/plugin_messages.h"
+#include "content/plugin/npobject_util.h"
+#include "content/plugin/plugin_channel_base.h"
+#include "content/plugin/plugin_thread.h"
+#include "third_party/npapi/bindings/npapi.h"
+#include "third_party/npapi/bindings/npruntime.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebBindings.h"
+#include "webkit/plugins/npapi/plugin_constants_win.h"
+
+using WebKit::WebBindings;
+
+NPObjectStub::NPObjectStub(
+ NPObject* npobject,
+ PluginChannelBase* channel,
+ int route_id,
+ gfx::NativeViewId containing_window,
+ const GURL& page_url)
+ : npobject_(npobject),
+ channel_(channel),
+ route_id_(route_id),
+ containing_window_(containing_window),
+ page_url_(page_url) {
+ 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_);
+ if (npobject_)
+ WebBindings::releaseObject(npobject_);
+}
+
+bool NPObjectStub::Send(IPC::Message* msg) {
+ return channel_->Send(msg);
+}
+
+void NPObjectStub::OnPluginDestroyed() {
+ // We null out the underlying NPObject pointer since it's not valid anymore (
+ // ScriptController manually deleted the object). As a result,
+ // OnMessageReceived won't dispatch any more messages. Since this includes
+ // OnRelease, this object won't get deleted until OnChannelError which might
+ // not happen for a long time if this renderer process has a long lived
+ // plugin instance to the same process. So we delete this object manually.
+ npobject_ = NULL;
+ MessageLoop::current()->DeleteSoon(FROM_HERE, this);
+}
+
+NPObject* NPObjectStub::GetUnderlyingNPObject() {
+ return npobject_;
+}
+
+IPC::Channel::Listener* NPObjectStub::GetChannelListener() {
+ return static_cast<IPC::Channel::Listener*>(this);
+}
+
+bool NPObjectStub::OnMessageReceived(const IPC::Message& msg) {
+ content::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() {
+ // If npobject_ is NULLed out, that means a DeleteSoon is happening.
+ if (npobject_)
+ delete this;
+}
+
+void NPObjectStub::OnRelease(IPC::Message* reply_msg) {
+ Send(reply_msg);
+ delete this;
+}
+
+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) {
+ scoped_refptr<PluginChannelBase> local_channel = channel_;
+ 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], local_channel, &(args_var[i]), containing_window_,
+ page_url_)) {
+ NPObjectMsg_Invoke::WriteReplyParams(reply_msg, result_param,
+ return_value);
+ local_channel->Send(reply_msg);
+ 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, local_channel, &result_param, true, containing_window_,
+ page_url_);
+ NPObjectMsg_Invoke::WriteReplyParams(reply_msg, result_param, return_value);
+ local_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_, property, true, containing_window_, page_url_);
+}
+
+void NPObjectStub::OnSetProperty(const NPIdentifier_Param& name,
+ const NPVariant_Param& property,
+ IPC::Message* reply_msg) {
+ bool result = false;
+ NPVariant result_var;
+ VOID_TO_NPVARIANT(result_var);
+ NPIdentifier id = CreateNPIdentifier(name);
+ NPVariant property_var;
+ if (!CreateNPVariant(
+ property, channel_, &property_var, containing_window_, page_url_)) {
+ NPObjectMsg_SetProperty::WriteReplyParams(reply_msg, result);
+ channel_->Send(reply_msg);
+ return;
+ }
+
+ if (IsPluginProcess()) {
+ if (npobject_->_class->setProperty) {
+#if defined(OS_WIN)
+ static std::wstring filename = StringToLowerASCII(
+ PluginThread::current()->plugin_path().BaseName().value());
+ static NPIdentifier fullscreen =
+ WebBindings::getStringIdentifier("fullScreen");
+ if (filename == webkit::npapi::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], &param);
+ value->push_back(param);
+ }
+
+ NPN_MemFree(value_np);
+}
+
+void NPObjectStub::OnConstruct(const std::vector<NPVariant_Param>& args,
+ IPC::Message* reply_msg) {
+ scoped_refptr<PluginChannelBase> local_channel = channel_;
+ 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], local_channel, &(args_var[i]), containing_window_,
+ page_url_)) {
+ NPObjectMsg_Invoke::WriteReplyParams(reply_msg, result_param,
+ return_value);
+ local_channel->Send(reply_msg);
+ 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, local_channel, &result_param, true, containing_window_,
+ page_url_);
+ NPObjectMsg_Invoke::WriteReplyParams(reply_msg, result_param, return_value);
+ local_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;
+ }
+
+ // Grab a reference to the underlying channel, as the NPObjectStub
+ // instance can be destroyed in the context of NPN_Evaluate. This
+ // can happen if the containing plugin instance is destroyed in
+ // NPN_Evaluate.
+ scoped_refptr<PluginChannelBase> local_channel = channel_;
+
+ 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, local_channel, &result_param, true, containing_window_,
+ page_url_);
+ NPObjectMsg_Evaluate::WriteReplyParams(reply_msg, result_param, return_value);
+ local_channel->Send(reply_msg);
+}
diff --git a/content/plugin/npobject_stub.h b/content/plugin/npobject_stub.h
new file mode 100644
index 0000000..a7637d66
--- /dev/null
+++ b/content/plugin/npobject_stub.h
@@ -0,0 +1,95 @@
+// Copyright (c) 2006-2008 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.
+//
+// A class that receives IPC messages from an NPObjectProxy and calls the real
+// NPObject.
+
+#ifndef CONTENT_PLUGIN_NPOBJECT_STUB_H_
+#define CONTENT_PLUGIN_NPOBJECT_STUB_H_
+#pragma once
+
+#include <vector>
+
+#include "base/ref_counted.h"
+#include "base/weak_ptr.h"
+#include "content/plugin/npobject_base.h"
+#include "googleurl/src/gurl.h"
+#include "ipc/ipc_channel.h"
+#include "ui/gfx/native_widget_types.h"
+
+class PluginChannelBase;
+struct NPIdentifier_Param;
+struct NPObject;
+struct NPVariant_Param;
+
+// This wraps an NPObject and converts IPC messages from NPObjectProxy to calls
+// to the object. The results are marshalled back. See npobject_proxy.h for
+// more information.
+class NPObjectStub : public IPC::Channel::Listener,
+ public IPC::Message::Sender,
+ public base::SupportsWeakPtr<NPObjectStub>,
+ public NPObjectBase {
+ public:
+ NPObjectStub(NPObject* npobject,
+ PluginChannelBase* channel,
+ int route_id,
+ gfx::NativeViewId containing_window,
+ const GURL& page_url);
+ ~NPObjectStub();
+
+ // IPC::Message::Sender implementation:
+ virtual bool Send(IPC::Message* msg);
+
+ // Called when the plugin widget that this NPObject came from is destroyed.
+ // This is needed because the renderer calls NPN_DeallocateObject on the
+ // window script object on destruction to avoid leaks.
+ void OnPluginDestroyed();
+
+ // NPObjectBase implementation.
+ virtual NPObject* GetUnderlyingNPObject();
+
+ virtual IPC::Channel::Listener* GetChannelListener();
+
+ private:
+ // IPC::Channel::Listener implementation:
+ virtual bool OnMessageReceived(const IPC::Message& message);
+ virtual void OnChannelError();
+
+ // message handlers
+ void OnRelease(IPC::Message* reply_msg);
+ void OnHasMethod(const NPIdentifier_Param& name,
+ bool* result);
+ void OnInvoke(bool is_default,
+ const NPIdentifier_Param& method,
+ const std::vector<NPVariant_Param>& args,
+ IPC::Message* reply_msg);
+ void OnHasProperty(const NPIdentifier_Param& name,
+ bool* result);
+ void OnGetProperty(const NPIdentifier_Param& name,
+ NPVariant_Param* property,
+ bool* result);
+ void OnSetProperty(const NPIdentifier_Param& name,
+ const NPVariant_Param& property,
+ IPC::Message* reply_msg);
+ void OnRemoveProperty(const NPIdentifier_Param& name,
+ bool* result);
+ void OnInvalidate();
+ void OnEnumeration(std::vector<NPIdentifier_Param>* value,
+ bool* result);
+ void OnConstruct(const std::vector<NPVariant_Param>& args,
+ IPC::Message* reply_msg);
+ void OnEvaluate(const std::string& script, bool popups_allowed,
+ IPC::Message* reply_msg);
+
+ private:
+ NPObject* npobject_;
+ scoped_refptr<PluginChannelBase> channel_;
+ int route_id_;
+ gfx::NativeViewId containing_window_;
+
+ // The url of the main frame hosting the plugin.
+ GURL page_url_;
+};
+
+#endif // CONTENT_PLUGIN_NPOBJECT_STUB_H_
diff --git a/content/plugin/npobject_util.cc b/content/plugin/npobject_util.cc
new file mode 100644
index 0000000..c9a2a68
--- /dev/null
+++ b/content/plugin/npobject_util.cc
@@ -0,0 +1,273 @@
+// Copyright (c) 2010 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/plugin/npobject_util.h"
+
+#include "base/string_util.h"
+#include "content/common/plugin_messages.h"
+#include "content/plugin/npobject_proxy.h"
+#include "content/plugin/plugin_channel_base.h"
+#include "third_party/npapi/bindings/nphostapi.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebBindings.h"
+#include "webkit/plugins/npapi/plugin_host.h"
+#include "webkit/glue/webkit_glue.h"
+
+using WebKit::WebBindings;
+
+// true if the current process is a plugin process, false if it's a renderer
+// process.
+static bool g_plugin_process;
+
+namespace {
+// 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()) {
+ PluginChannelBase* renderer_channel =
+ PluginChannelBase::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;
+}
+
+}
+
+void PatchNPNFunctions() {
+ g_plugin_process = true;
+ NPNetscapeFuncs* funcs = GetHostFunctions();
+ webkit::npapi::PluginHost::Singleton()->PatchNPNetscapeFuncs(funcs);
+}
+
+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,
+ PluginChannelBase* channel,
+ NPVariant_Param* param,
+ bool release,
+ gfx::NativeViewId containing_window,
+ 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);
+ 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->GenerateRouteID();
+ new NPObjectStub(
+ variant.value.objectValue, channel, route_id, containing_window,
+ page_url);
+ param->npobject_routing_id = route_id;
+ } else {
+ param->type = NPVARIANT_PARAM_VOID;
+ }
+ }
+ break;
+ }
+ default:
+ NOTREACHED();
+ }
+
+ if (release)
+ WebBindings::releaseVariantValue(const_cast<NPVariant*>(&variant));
+}
+
+bool CreateNPVariant(const NPVariant_Param& param,
+ PluginChannelBase* channel,
+ NPVariant* result,
+ gfx::NativeViewId containing_window,
+ 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;
+ result->value.stringValue.UTF8Characters =
+ static_cast<NPUTF8 *>(base::strdup(param.string_value.c_str()));
+ result->value.stringValue.UTF8Length =
+ static_cast<int>(param.string_value.size());
+ break;
+ case NPVARIANT_PARAM_SENDER_OBJECT_ROUTING_ID:
+ result->type = NPVariantType_Object;
+ result->value.objectValue =
+ NPObjectProxy::Create(channel,
+ param.npobject_routing_id,
+ containing_window,
+ page_url);
+ 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;
+}
diff --git a/content/plugin/npobject_util.h b/content/plugin/npobject_util.h
new file mode 100644
index 0000000..cca210d
--- /dev/null
+++ b/content/plugin/npobject_util.h
@@ -0,0 +1,73 @@
+// Copyright (c) 2006-2008 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.
+//
+// Helper functions that are used by the NPObject proxy and stub.
+
+#ifndef CONTENT_PLUGIN_NPOBJECT_UTIL_H_
+#define CONTENT_PLUGIN_NPOBJECT_UTIL_H_
+#pragma once
+
+#include "build/build_config.h"
+
+#if defined(OS_WIN)
+#include <windows.h>
+#endif
+
+#include "content/plugin/npobject_stub.h"
+
+class GURL;
+class NPObjectProxy;
+class PluginChannelBase;
+
+struct _NPVariant;
+struct NPIdentifier_Param;
+struct NPVariant_Param;
+
+typedef _NPVariant NPVariant;
+typedef void *NPIdentifier;
+
+// Needs to be called early in the plugin process lifetime, before any
+// plugin instances are initialized.
+void PatchNPNFunctions();
+
+// Returns true if the current process is a plugin process, or false if it's a
+// renderer process.
+bool IsPluginProcess();
+
+// Creates an object similar to NPIdentifier that can be marshalled.
+void CreateNPIdentifierParam(NPIdentifier id, NPIdentifier_Param* param);
+
+// Creates an NPIdentifier from the marshalled object.
+NPIdentifier CreateNPIdentifier(const NPIdentifier_Param& param);
+
+// Creates an object similar to NPVariant that can be marshalled.
+// If the containing NPObject happens to be an NPObject, then a stub
+// is created around it and param holds the routing id for it.
+// If release is true, the NPVariant object is released (except if
+// it contains an NPObject, since the stub will manage its lifetime).
+void CreateNPVariantParam(const NPVariant& variant,
+ PluginChannelBase* channel,
+ NPVariant_Param* param,
+ bool release,
+ gfx::NativeViewId containing_window,
+ const GURL& page_url);
+
+// Creates an NPVariant from the marshalled object.
+// Returns true on success.
+bool CreateNPVariant(const NPVariant_Param& param,
+ PluginChannelBase* channel,
+ NPVariant* result,
+ gfx::NativeViewId containing_window,
+ const GURL& page_url);
+
+#if defined(OS_WIN)
+// Given a plugin's HWND, returns an event associated with the TabContents
+// that's set when inside a messagebox. This tells the plugin process that
+// the message queue should be pumped (as what would happen if everything was
+// in-process). This avoids deadlocks when a plugin invokes javascript that
+// causes a message box to come up.
+HANDLE GetMessageBoxEvent(HWND hwnd);
+#endif // defined(OS_WIN)
+
+#endif // CONTENT_PLUGIN_NPOBJECT_UTIL_H_
diff --git a/content/plugin/plugin_channel.cc b/content/plugin/plugin_channel.cc
new file mode 100644
index 0000000..8d90601
--- /dev/null
+++ b/content/plugin/plugin_channel.cc
@@ -0,0 +1,328 @@
+// Copyright (c) 2010 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/plugin/plugin_channel.h"
+
+#include "base/command_line.h"
+#include "base/process_util.h"
+#include "base/string_util.h"
+#include "base/synchronization/lock.h"
+#include "base/synchronization/waitable_event.h"
+#include "build/build_config.h"
+#include "content/common/child_process.h"
+#include "content/common/content_switches.h"
+#include "content/common/plugin_messages.h"
+#include "content/plugin/plugin_thread.h"
+#include "content/plugin/webplugin_delegate_stub.h"
+#include "content/plugin/webplugin_proxy.h"
+#include "webkit/plugins/npapi/plugin_instance.h"
+
+#if defined(OS_POSIX)
+#include "base/eintr_wrapper.h"
+#include "ipc/ipc_channel_posix.h"
+#endif
+
+namespace {
+
+class PluginReleaseTask : public Task {
+ public:
+ void Run() {
+ ChildProcess::current()->ReleaseProcess();
+ }
+};
+
+// How long we wait before releasing the plugin process.
+const int kPluginReleaseTimeMs = 5 * 60 * 1000; // 5 minutes
+
+} // namespace
+
+// If a sync call to the renderer results in a modal dialog, we need to have a
+// way to know so that we can run a nested message loop to simulate what would
+// happen in a single process browser and avoid deadlock.
+class PluginChannel::MessageFilter : public IPC::ChannelProxy::MessageFilter {
+ public:
+ MessageFilter() : channel_(NULL) { }
+ ~MessageFilter() {
+ // Clean up in case of renderer crash.
+ for (ModalDialogEventMap::iterator i = modal_dialog_event_map_.begin();
+ i != modal_dialog_event_map_.end(); ++i) {
+ delete i->second.event;
+ }
+ }
+
+ base::WaitableEvent* GetModalDialogEvent(
+ gfx::NativeViewId containing_window) {
+ base::AutoLock auto_lock(modal_dialog_event_map_lock_);
+ if (!modal_dialog_event_map_.count(containing_window)) {
+ NOTREACHED();
+ return NULL;
+ }
+
+ return modal_dialog_event_map_[containing_window].event;
+ }
+
+ // Decrement the ref count associated with the modal dialog event for the
+ // given tab.
+ void ReleaseModalDialogEvent(gfx::NativeViewId containing_window) {
+ base::AutoLock auto_lock(modal_dialog_event_map_lock_);
+ if (!modal_dialog_event_map_.count(containing_window)) {
+ NOTREACHED();
+ return;
+ }
+
+ if (--(modal_dialog_event_map_[containing_window].refcount))
+ return;
+
+ // Delete the event when the stack unwinds as it could be in use now.
+ MessageLoop::current()->DeleteSoon(
+ FROM_HERE, modal_dialog_event_map_[containing_window].event);
+ modal_dialog_event_map_.erase(containing_window);
+ }
+
+ bool Send(IPC::Message* message) {
+ // Need this function for the IPC_MESSAGE_HANDLER_DELAY_REPLY macro.
+ return channel_->Send(message);
+ }
+
+ private:
+ void OnFilterAdded(IPC::Channel* channel) { channel_ = channel; }
+
+ bool OnMessageReceived(const IPC::Message& message) {
+ IPC_BEGIN_MESSAGE_MAP(PluginChannel::MessageFilter, message)
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(PluginMsg_Init, OnInit)
+ IPC_MESSAGE_HANDLER(PluginMsg_SignalModalDialogEvent,
+ OnSignalModalDialogEvent)
+ IPC_MESSAGE_HANDLER(PluginMsg_ResetModalDialogEvent,
+ OnResetModalDialogEvent)
+ IPC_END_MESSAGE_MAP()
+ return message.type() == PluginMsg_SignalModalDialogEvent::ID ||
+ message.type() == PluginMsg_ResetModalDialogEvent::ID;
+ }
+
+ void OnInit(const PluginMsg_Init_Params& params, IPC::Message* reply_msg) {
+ base::AutoLock auto_lock(modal_dialog_event_map_lock_);
+ if (modal_dialog_event_map_.count(params.containing_window)) {
+ modal_dialog_event_map_[params.containing_window].refcount++;
+ return;
+ }
+
+ WaitableEventWrapper wrapper;
+ wrapper.event = new base::WaitableEvent(true, false);
+ wrapper.refcount = 1;
+ modal_dialog_event_map_[params.containing_window] = wrapper;
+ }
+
+ void OnSignalModalDialogEvent(gfx::NativeViewId containing_window) {
+ base::AutoLock auto_lock(modal_dialog_event_map_lock_);
+ if (modal_dialog_event_map_.count(containing_window))
+ modal_dialog_event_map_[containing_window].event->Signal();
+ }
+
+ void OnResetModalDialogEvent(gfx::NativeViewId containing_window) {
+ base::AutoLock auto_lock(modal_dialog_event_map_lock_);
+ if (modal_dialog_event_map_.count(containing_window))
+ modal_dialog_event_map_[containing_window].event->Reset();
+ }
+
+ struct WaitableEventWrapper {
+ base::WaitableEvent* event;
+ int refcount; // There could be multiple plugin instances per tab.
+ };
+ typedef std::map<gfx::NativeViewId, WaitableEventWrapper> ModalDialogEventMap;
+ ModalDialogEventMap modal_dialog_event_map_;
+ base::Lock modal_dialog_event_map_lock_;
+
+ IPC::Channel* channel_;
+};
+
+PluginChannel* PluginChannel::GetPluginChannel(int renderer_id,
+ MessageLoop* ipc_message_loop) {
+ // Map renderer ID to a (single) channel to that process.
+ std::string channel_key = StringPrintf(
+ "%d.r%d", base::GetCurrentProcId(), renderer_id);
+
+ PluginChannel* channel =
+ static_cast<PluginChannel*>(PluginChannelBase::GetChannel(
+ channel_key,
+ IPC::Channel::MODE_SERVER,
+ ClassFactory,
+ ipc_message_loop,
+ false));
+
+ if (channel)
+ channel->renderer_id_ = renderer_id;
+
+ return channel;
+}
+
+// static
+void PluginChannel::NotifyRenderersOfPendingShutdown() {
+ Broadcast(new PluginHostMsg_PluginShuttingDown());
+}
+
+PluginChannel::PluginChannel()
+ : renderer_handle_(0),
+ renderer_id_(-1),
+ in_send_(0),
+ incognito_(false),
+ filter_(new MessageFilter()) {
+ set_send_unblocking_only_during_unblock_dispatch();
+ ChildProcess::current()->AddRefProcess();
+ const CommandLine* command_line = CommandLine::ForCurrentProcess();
+ log_messages_ = command_line->HasSwitch(switches::kLogPluginMessages);
+}
+
+PluginChannel::~PluginChannel() {
+ if (renderer_handle_)
+ base::CloseProcessHandle(renderer_handle_);
+
+ MessageLoop::current()->PostDelayedTask(FROM_HERE, new PluginReleaseTask(),
+ kPluginReleaseTimeMs);
+}
+
+bool PluginChannel::Send(IPC::Message* msg) {
+ in_send_++;
+ if (log_messages_) {
+ VLOG(1) << "sending message @" << msg << " on channel @" << this
+ << " with type " << msg->type();
+ }
+ bool result = PluginChannelBase::Send(msg);
+ in_send_--;
+ return result;
+}
+
+bool PluginChannel::OnMessageReceived(const IPC::Message& msg) {
+ if (log_messages_) {
+ VLOG(1) << "received message @" << &msg << " on channel @" << this
+ << " with type " << msg.type();
+ }
+ return PluginChannelBase::OnMessageReceived(msg);
+}
+
+bool PluginChannel::OnControlMessageReceived(const IPC::Message& msg) {
+ bool handled = true;
+ IPC_BEGIN_MESSAGE_MAP(PluginChannel, msg)
+ IPC_MESSAGE_HANDLER(PluginMsg_CreateInstance, OnCreateInstance)
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(PluginMsg_DestroyInstance,
+ OnDestroyInstance)
+ IPC_MESSAGE_HANDLER(PluginMsg_GenerateRouteID, OnGenerateRouteID)
+ IPC_MESSAGE_HANDLER(PluginMsg_ClearSiteData, OnClearSiteData)
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP()
+ DCHECK(handled);
+ return handled;
+}
+
+void PluginChannel::OnCreateInstance(const std::string& mime_type,
+ int* instance_id) {
+ *instance_id = GenerateRouteID();
+ scoped_refptr<WebPluginDelegateStub> stub(new WebPluginDelegateStub(
+ mime_type, *instance_id, this));
+ AddRoute(*instance_id, stub, NULL);
+ plugin_stubs_.push_back(stub);
+}
+
+void PluginChannel::OnDestroyInstance(int instance_id,
+ IPC::Message* reply_msg) {
+ for (size_t i = 0; i < plugin_stubs_.size(); ++i) {
+ if (plugin_stubs_[i]->instance_id() == instance_id) {
+ scoped_refptr<MessageFilter> filter(filter_);
+ gfx::NativeViewId window =
+ plugin_stubs_[i]->webplugin()->containing_window();
+ plugin_stubs_.erase(plugin_stubs_.begin() + i);
+ Send(reply_msg);
+ RemoveRoute(instance_id);
+ // NOTE: *this* might be deleted as a result of calling RemoveRoute.
+ // Don't release the modal dialog event right away, but do it after the
+ // stack unwinds since the plugin can be destroyed later if it's in use
+ // right now.
+ MessageLoop::current()->PostNonNestableTask(FROM_HERE, NewRunnableMethod(
+ filter.get(), &MessageFilter::ReleaseModalDialogEvent, window));
+ return;
+ }
+ }
+
+ NOTREACHED() << "Couldn't find WebPluginDelegateStub to destroy";
+}
+
+void PluginChannel::OnGenerateRouteID(int* route_id) {
+ *route_id = GenerateRouteID();
+}
+
+int PluginChannel::GenerateRouteID() {
+ static int last_id = 0;
+ return ++last_id;
+}
+
+void PluginChannel::OnClearSiteData(const std::string& site,
+ uint64 flags,
+ base::Time begin_time) {
+ bool success = false;
+ CommandLine* command_line = CommandLine::ForCurrentProcess();
+ FilePath path = command_line->GetSwitchValuePath(switches::kPluginPath);
+ scoped_refptr<webkit::npapi::PluginLib> plugin_lib(
+ webkit::npapi::PluginLib::CreatePluginLib(path));
+ if (plugin_lib.get()) {
+ NPError err = plugin_lib->NP_Initialize();
+ if (err == NPERR_NO_ERROR) {
+ const char* site_str = site.empty() ? NULL : site.c_str();
+ uint64 max_age;
+ if (begin_time > base::Time()) {
+ base::TimeDelta delta = base::Time::Now() - begin_time;
+ max_age = delta.InSeconds();
+ } else {
+ max_age = kuint64max;
+ }
+ err = plugin_lib->NP_ClearSiteData(site_str, flags, max_age);
+ success = (err == NPERR_NO_ERROR);
+ }
+ }
+ Send(new PluginHostMsg_ClearSiteDataResult(success));
+}
+
+base::WaitableEvent* PluginChannel::GetModalDialogEvent(
+ gfx::NativeViewId containing_window) {
+ return filter_->GetModalDialogEvent(containing_window);
+}
+
+void PluginChannel::OnChannelConnected(int32 peer_pid) {
+ base::ProcessHandle handle;
+ if (!base::OpenProcessHandle(peer_pid, &handle)) {
+ NOTREACHED();
+ }
+ renderer_handle_ = handle;
+ PluginChannelBase::OnChannelConnected(peer_pid);
+}
+
+void PluginChannel::OnChannelError() {
+ base::CloseProcessHandle(renderer_handle_);
+ renderer_handle_ = 0;
+ PluginChannelBase::OnChannelError();
+ CleanUp();
+}
+
+void PluginChannel::CleanUp() {
+ // We need to clean up the stubs so that they call NPPDestroy. This will
+ // also lead to them releasing their reference on this object so that it can
+ // be deleted.
+ for (size_t i = 0; i < plugin_stubs_.size(); ++i)
+ RemoveRoute(plugin_stubs_[i]->instance_id());
+
+ // Need to addref this object temporarily because otherwise removing the last
+ // stub will cause the destructor of this object to be called, however at
+ // that point plugin_stubs_ will have one element and its destructor will be
+ // called twice.
+ scoped_refptr<PluginChannel> me(this);
+
+ plugin_stubs_.clear();
+}
+
+bool PluginChannel::Init(MessageLoop* ipc_message_loop, bool create_pipe_now) {
+ if (!PluginChannelBase::Init(ipc_message_loop, create_pipe_now))
+ return false;
+
+ channel_->AddFilter(filter_.get());
+ return true;
+}
+
diff --git a/content/plugin/plugin_channel.h b/content/plugin/plugin_channel.h
new file mode 100644
index 0000000..6b1d278
--- /dev/null
+++ b/content/plugin/plugin_channel.h
@@ -0,0 +1,99 @@
+// Copyright (c) 2010 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.
+
+#ifndef CONTENT_PLUGIN_PLUGIN_CHANNEL_H_
+#define CONTENT_PLUGIN_PLUGIN_CHANNEL_H_
+#pragma once
+
+#include <vector>
+#include "base/ref_counted.h"
+#include "base/scoped_handle.h"
+#include "build/build_config.h"
+#include "content/plugin/plugin_channel_base.h"
+#include "content/plugin/webplugin_delegate_stub.h"
+
+namespace base {
+class WaitableEvent;
+}
+
+// Encapsulates an IPC channel between the plugin process and one renderer
+// process. On the renderer side there's a corresponding PluginChannelHost.
+class PluginChannel : public PluginChannelBase {
+ public:
+ // Get a new PluginChannel object for the current process to talk to the
+ // given renderer process. The renderer ID is an opaque unique ID generated
+ // by the browser.
+ static PluginChannel* GetPluginChannel(int renderer_id,
+ MessageLoop* ipc_message_loop);
+
+ // Send a message to all renderers that the process is going to shutdown.
+ static void NotifyRenderersOfPendingShutdown();
+
+ virtual ~PluginChannel();
+
+ virtual bool Send(IPC::Message* msg);
+ virtual bool OnMessageReceived(const IPC::Message& message);
+
+ base::ProcessHandle renderer_handle() const { return renderer_handle_; }
+ int renderer_id() { return renderer_id_; }
+
+ virtual int GenerateRouteID();
+
+ // Returns the event that's set when a call to the renderer causes a modal
+ // dialog to come up.
+ base::WaitableEvent* GetModalDialogEvent(gfx::NativeViewId containing_window);
+
+ bool in_send() { return in_send_ != 0; }
+
+ bool incognito() { return incognito_; }
+ void set_incognito(bool value) { incognito_ = value; }
+
+#if defined(OS_POSIX)
+ int renderer_fd() const { return channel_->GetClientFileDescriptor(); }
+#endif
+
+ protected:
+ // IPC::Channel::Listener implementation:
+ virtual void OnChannelConnected(int32 peer_pid);
+ virtual void OnChannelError();
+
+ virtual void CleanUp();
+
+ // Overrides PluginChannelBase::Init.
+ virtual bool Init(MessageLoop* ipc_message_loop, bool create_pipe_now);
+
+ private:
+ class MessageFilter;
+
+ // Called on the plugin thread
+ PluginChannel();
+
+ virtual bool OnControlMessageReceived(const IPC::Message& msg);
+
+ static PluginChannelBase* ClassFactory() { return new PluginChannel(); }
+
+ void OnCreateInstance(const std::string& mime_type, int* instance_id);
+ void OnDestroyInstance(int instance_id, IPC::Message* reply_msg);
+ void OnGenerateRouteID(int* route_id);
+ void OnClearSiteData(const std::string& site,
+ uint64 flags,
+ base::Time begin_time);
+
+ std::vector<scoped_refptr<WebPluginDelegateStub> > plugin_stubs_;
+
+ // Handle to the renderer process who is on the other side of the channel.
+ base::ProcessHandle renderer_handle_;
+
+ // The id of the renderer who is on the other side of the channel.
+ int renderer_id_;
+
+ int in_send_; // Tracks if we're in a Send call.
+ bool log_messages_; // True if we should log sent and received messages.
+ bool incognito_; // True if the renderer is in incognito mode.
+ scoped_refptr<MessageFilter> filter_; // Handles the modal dialog events.
+
+ DISALLOW_COPY_AND_ASSIGN(PluginChannel);
+};
+
+#endif // CONTENT_PLUGIN_PLUGIN_CHANNEL_H_
diff --git a/content/plugin/plugin_channel_base.cc b/content/plugin/plugin_channel_base.cc
new file mode 100644
index 0000000..d78d48c
--- /dev/null
+++ b/content/plugin/plugin_channel_base.cc
@@ -0,0 +1,241 @@
+// Copyright (c) 2009 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/plugin/plugin_channel_base.h"
+
+#include <stack>
+
+#include "base/auto_reset.h"
+#include "base/hash_tables.h"
+#include "base/lazy_instance.h"
+#include "base/string_number_conversions.h"
+#include "content/common/child_process.h"
+#include "ipc/ipc_sync_message.h"
+
+#if defined(OS_POSIX)
+#include "ipc/ipc_channel_posix.h"
+#endif
+
+typedef base::hash_map<std::string, scoped_refptr<PluginChannelBase> >
+ PluginChannelMap;
+
+static PluginChannelMap g_plugin_channels_;
+
+static base::LazyInstance<std::stack<scoped_refptr<PluginChannelBase> > >
+ lazy_plugin_channel_stack_(base::LINKER_INITIALIZED);
+
+static int next_pipe_id = 0;
+
+PluginChannelBase* PluginChannelBase::GetChannel(
+ const IPC::ChannelHandle& channel_handle, IPC::Channel::Mode mode,
+ PluginChannelFactory factory, MessageLoop* ipc_message_loop,
+ bool create_pipe_now) {
+ scoped_refptr<PluginChannelBase> channel;
+ std::string channel_key = channel_handle.name;
+ PluginChannelMap::const_iterator iter = g_plugin_channels_.find(channel_key);
+ if (iter == g_plugin_channels_.end()) {
+ channel = factory();
+ } else {
+ channel = iter->second;
+ }
+
+ DCHECK(channel != NULL);
+
+ if (!channel->channel_valid()) {
+ channel->channel_handle_ = channel_handle;
+ if (mode & IPC::Channel::MODE_SERVER_FLAG) {
+ channel->channel_handle_.name.append(".");
+ channel->channel_handle_.name.append(base::IntToString(next_pipe_id++));
+ }
+ channel->mode_ = mode;
+ if (channel->Init(ipc_message_loop, create_pipe_now)) {
+ g_plugin_channels_[channel_key] = channel;
+ } else {
+ channel = NULL;
+ }
+ }
+
+ return channel;
+}
+
+void PluginChannelBase::Broadcast(IPC::Message* message) {
+ for (PluginChannelMap::iterator iter = g_plugin_channels_.begin();
+ iter != g_plugin_channels_.end();
+ ++iter) {
+ iter->second->Send(new IPC::Message(*message));
+ }
+ delete message;
+}
+
+PluginChannelBase::PluginChannelBase()
+ : mode_(IPC::Channel::MODE_NONE),
+ plugin_count_(0),
+ peer_pid_(0),
+ in_remove_route_(false),
+ channel_valid_(false),
+ in_unblock_dispatch_(0),
+ send_unblocking_only_during_unblock_dispatch_(false) {
+}
+
+PluginChannelBase::~PluginChannelBase() {
+}
+
+PluginChannelBase* PluginChannelBase::GetCurrentChannel() {
+ return lazy_plugin_channel_stack_.Pointer()->top();
+}
+
+void PluginChannelBase::CleanupChannels() {
+ // Make a copy of the references as we can't iterate the map since items will
+ // be removed from it as we clean them up.
+ std::vector<scoped_refptr<PluginChannelBase> > channels;
+ for (PluginChannelMap::const_iterator iter = g_plugin_channels_.begin();
+ iter != g_plugin_channels_.end();
+ ++iter) {
+ channels.push_back(iter->second);
+ }
+
+ for (size_t i = 0; i < channels.size(); ++i)
+ channels[i]->CleanUp();
+
+ // This will clean up channels added to the map for which subsequent
+ // AddRoute wasn't called
+ g_plugin_channels_.clear();
+}
+
+NPObjectBase* PluginChannelBase::GetNPObjectListenerForRoute(int route_id) {
+ ListenerMap::iterator iter = npobject_listeners_.find(route_id);
+ if (iter == npobject_listeners_.end()) {
+ DLOG(WARNING) << "Invalid route id passed in:" << route_id;
+ return NULL;
+ }
+ return iter->second;
+}
+
+bool PluginChannelBase::Init(MessageLoop* ipc_message_loop,
+ bool create_pipe_now) {
+ channel_.reset(new IPC::SyncChannel(
+ channel_handle_, mode_, this, ipc_message_loop, create_pipe_now,
+ ChildProcess::current()->GetShutDownEvent()));
+ channel_valid_ = true;
+ return true;
+}
+
+bool PluginChannelBase::Send(IPC::Message* message) {
+ if (!channel_.get()) {
+ delete message;
+ return false;
+ }
+
+ if (send_unblocking_only_during_unblock_dispatch_ &&
+ in_unblock_dispatch_ == 0 &&
+ message->is_sync()) {
+ message->set_unblock(false);
+ }
+
+ return channel_->Send(message);
+}
+
+int PluginChannelBase::Count() {
+ return static_cast<int>(g_plugin_channels_.size());
+}
+
+bool PluginChannelBase::OnMessageReceived(const IPC::Message& message) {
+ // This call might cause us to be deleted, so keep an extra reference to
+ // ourself so that we can send the reply and decrement back in_dispatch_.
+ lazy_plugin_channel_stack_.Pointer()->push(
+ scoped_refptr<PluginChannelBase>(this));
+
+ bool handled;
+ if (message.should_unblock())
+ in_unblock_dispatch_++;
+ if (message.routing_id() == MSG_ROUTING_CONTROL) {
+ handled = OnControlMessageReceived(message);
+ } else {
+ handled = router_.RouteMessage(message);
+ if (!handled && message.is_sync()) {
+ // The listener has gone away, so we must respond or else the caller will
+ // hang waiting for a reply.
+ IPC::Message* reply = IPC::SyncMessage::GenerateReply(&message);
+ reply->set_reply_error();
+ Send(reply);
+ }
+ }
+ if (message.should_unblock())
+ in_unblock_dispatch_--;
+
+ lazy_plugin_channel_stack_.Pointer()->pop();
+ return handled;
+}
+
+void PluginChannelBase::OnChannelConnected(int32 peer_pid) {
+ peer_pid_ = peer_pid;
+}
+
+void PluginChannelBase::AddRoute(int route_id,
+ IPC::Channel::Listener* listener,
+ NPObjectBase* npobject) {
+ if (npobject) {
+ npobject_listeners_[route_id] = npobject;
+ } else {
+ plugin_count_++;
+ }
+
+ router_.AddRoute(route_id, listener);
+}
+
+void PluginChannelBase::RemoveRoute(int route_id) {
+ router_.RemoveRoute(route_id);
+
+ ListenerMap::iterator iter = npobject_listeners_.find(route_id);
+ if (iter != npobject_listeners_.end()) {
+ // This was an NPObject proxy or stub, it's not involved in the refcounting.
+
+ // If this RemoveRoute call from the NPObject is a result of us calling
+ // OnChannelError below, don't call erase() here because that'll corrupt
+ // the iterator below.
+ if (in_remove_route_) {
+ iter->second = NULL;
+ } else {
+ npobject_listeners_.erase(iter);
+ }
+
+ return;
+ }
+
+ plugin_count_--;
+ DCHECK(plugin_count_ >= 0);
+
+ if (!plugin_count_) {
+ AutoReset<bool> auto_reset_in_remove_route(&in_remove_route_, true);
+ for (ListenerMap::iterator npobj_iter = npobject_listeners_.begin();
+ npobj_iter != npobject_listeners_.end(); ++npobj_iter) {
+ if (npobj_iter->second) {
+ IPC::Channel::Listener* channel_listener =
+ npobj_iter->second->GetChannelListener();
+ DCHECK(channel_listener != NULL);
+ channel_listener->OnChannelError();
+ }
+ }
+
+ for (PluginChannelMap::iterator iter = g_plugin_channels_.begin();
+ iter != g_plugin_channels_.end(); ++iter) {
+ if (iter->second == this) {
+ g_plugin_channels_.erase(iter);
+ return;
+ }
+ }
+
+ NOTREACHED();
+ }
+}
+
+bool PluginChannelBase::OnControlMessageReceived(const IPC::Message& msg) {
+ NOTREACHED() <<
+ "should override in subclass if you care about control messages";
+ return false;
+}
+
+void PluginChannelBase::OnChannelError() {
+ channel_valid_ = false;
+}
diff --git a/content/plugin/plugin_channel_base.h b/content/plugin/plugin_channel_base.h
new file mode 100644
index 0000000..8d3cbc5
--- /dev/null
+++ b/content/plugin/plugin_channel_base.h
@@ -0,0 +1,149 @@
+// Copyright (c) 2006-2008 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.
+
+#ifndef CONTENT_PLUGIN_PLUGIN_CHANNEL_BASE_H_
+#define CONTENT_PLUGIN_PLUGIN_CHANNEL_BASE_H_
+#pragma once
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/hash_tables.h"
+#include "base/message_loop.h"
+#include "base/ref_counted.h"
+#include "base/scoped_ptr.h"
+#include "content/common/message_router.h"
+#include "content/plugin/npobject_base.h"
+#include "ipc/ipc_channel_handle.h"
+#include "ipc/ipc_sync_channel.h"
+#include "ui/gfx/native_widget_types.h"
+
+// Encapsulates an IPC channel between a renderer and a plugin process.
+class PluginChannelBase : public IPC::Channel::Listener,
+ public IPC::Message::Sender,
+ public base::RefCountedThreadSafe<PluginChannelBase> {
+ public:
+
+ // WebPlugin[Delegate] call these on construction and destruction to setup
+ // the routing and manage lifetime of this object. This is also called by
+ // NPObjectProxy and NPObjectStub. However the latter don't control the
+ // lifetime of this object (by passing true for npobject) because we don't
+ // want a leak of an NPObject in a plugin to keep the channel around longer
+ // than necessary.
+ void AddRoute(int route_id, IPC::Channel::Listener* listener,
+ NPObjectBase* npobject);
+ void RemoveRoute(int route_id);
+
+ // IPC::Message::Sender implementation:
+ virtual bool Send(IPC::Message* msg);
+
+ int peer_pid() { return peer_pid_; }
+ IPC::ChannelHandle channel_handle() const { return channel_handle_; }
+
+ // Returns the number of open plugin channels in this process.
+ static int Count();
+
+ // Returns a new route id.
+ virtual int GenerateRouteID() = 0;
+
+ // Returns whether the channel is valid or not. A channel is invalid
+ // if it is disconnected due to a channel error.
+ bool channel_valid() {
+ return channel_valid_;
+ }
+
+ // Returns the most recent PluginChannelBase to have received a message
+ // in this process.
+ static PluginChannelBase* GetCurrentChannel();
+
+ static void CleanupChannels();
+
+ // Returns the NPObjectBase object for the route id passed in.
+ // Returns NULL on failure.
+ NPObjectBase* GetNPObjectListenerForRoute(int route_id);
+
+ protected:
+ typedef PluginChannelBase* (*PluginChannelFactory)();
+
+ friend class base::RefCountedThreadSafe<PluginChannelBase>;
+
+ virtual ~PluginChannelBase();
+
+ // Returns a PluginChannelBase derived object for the given channel name.
+ // If an existing channel exists returns that object, otherwise creates a
+ // new one. Even though on creation the object is refcounted, each caller
+ // must still ref count the returned value. When there are no more routes
+ // on the channel and its ref count is 0, the object deletes itself.
+ static PluginChannelBase* GetChannel(
+ const IPC::ChannelHandle& channel_handle, IPC::Channel::Mode mode,
+ PluginChannelFactory factory, MessageLoop* ipc_message_loop,
+ bool create_pipe_now);
+
+ // Sends a message to all instances.
+ static void Broadcast(IPC::Message* message);
+
+ // Called on the worker thread
+ PluginChannelBase();
+
+ virtual void CleanUp() { }
+
+ // Implemented by derived classes to handle control messages
+ virtual bool OnControlMessageReceived(const IPC::Message& msg);
+
+ // IPC::Channel::Listener implementation:
+ virtual bool OnMessageReceived(const IPC::Message& msg);
+ virtual void OnChannelConnected(int32 peer_pid);
+ virtual void OnChannelError();
+
+ void set_send_unblocking_only_during_unblock_dispatch() {
+ send_unblocking_only_during_unblock_dispatch_ = true;
+ }
+
+ virtual bool Init(MessageLoop* ipc_message_loop, bool create_pipe_now);
+
+ scoped_ptr<IPC::SyncChannel> channel_;
+
+ private:
+ IPC::Channel::Mode mode_;
+ IPC::ChannelHandle channel_handle_;
+ int plugin_count_;
+ int peer_pid_;
+
+ // true when in the middle of a RemoveRoute call
+ bool in_remove_route_;
+
+ // Keep track of all the registered NPObjects proxies/stubs so that when the
+ // channel is closed we can inform them.
+ typedef base::hash_map<int, NPObjectBase*> ListenerMap;
+ ListenerMap npobject_listeners_;
+
+ // Used to implement message routing functionality to WebPlugin[Delegate]
+ // objects
+ MessageRouter router_;
+
+ // A channel is invalid if it is disconnected as a result of a channel
+ // error. This flag is used to indicate the same.
+ bool channel_valid_;
+
+ // Track whether we're dispatching a message with the unblock flag; works like
+ // a refcount, 0 when we're not.
+ int in_unblock_dispatch_;
+
+ // If true, sync messages will only be marked as unblocking if the channel is
+ // in the middle of dispatching an unblocking message.
+ // The plugin process wants to avoid setting the unblock flag on its sync
+ // messages unless necessary, since it can potentially introduce reentrancy
+ // into WebKit in ways that it doesn't expect (i.e. causing layout during
+ // paint). However to avoid deadlock, we must ensure that any message that's
+ // sent as a result of a sync call from the renderer must unblock the
+ // renderer. We additionally have to do this for async messages from the
+ // renderer that have the unblock flag set, since they could be followed by a
+ // sync message that won't get dispatched until the call to the renderer is
+ // complete.
+ bool send_unblocking_only_during_unblock_dispatch_;
+
+ DISALLOW_COPY_AND_ASSIGN(PluginChannelBase);
+};
+
+#endif // CONTENT_PLUGIN_PLUGIN_CHANNEL_BASE_H_
diff --git a/content/plugin/plugin_interpose_util_mac.h b/content/plugin/plugin_interpose_util_mac.h
new file mode 100644
index 0000000..2a715ba
--- /dev/null
+++ b/content/plugin/plugin_interpose_util_mac.h
@@ -0,0 +1,57 @@
+// Copyright (c) 2009 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.
+
+#ifndef CONTENT_PLUGIN_PLUGIN_INTERPOSE_UTIL_MAC_H_
+#define CONTENT_PLUGIN_PLUGIN_INTERPOSE_UTIL_MAC_H_
+#pragma once
+
+#include <Carbon/Carbon.h>
+
+#import "base/basictypes.h"
+
+// This is really a WebPluginDelegateImpl, but that class is private to the
+// framework, and these functions are called from a dylib.
+typedef void* OpaquePluginRef;
+
+namespace mac_plugin_interposing {
+
+// Swizzles methods we need to watch in order to manage process and window
+// focus correctly.
+void SetUpCocoaInterposing();
+
+// Brings the plugin process to the front so that the user can see its windows.
+void SwitchToPluginProcess();
+
+// Returns the delegate currently processing events.
+OpaquePluginRef GetActiveDelegate();
+
+// Sends a message to the browser process to inform it that the given window
+// has been brought forward.
+void NotifyBrowserOfPluginSelectWindow(uint32 window_id, CGRect bounds,
+ bool modal);
+
+// Sends a message to the browser process to inform it that the given window
+// has been shown.
+void NotifyBrowserOfPluginShowWindow(uint32 window_id, CGRect bounds,
+ bool modal);
+
+// Sends a message to the browser process to inform it that the given window
+// has been hidden, and switches focus back to the browser process if there are
+// no remaining plugin windows.
+void NotifyBrowserOfPluginHideWindow(uint32 window_id, CGRect bounds);
+
+// Sends a message to the plugin that a theme cursor was set.
+void NotifyPluginOfSetThemeCursor(OpaquePluginRef delegate,
+ ThemeCursor cursor);
+
+// Sends a message to the plugin that a cursor was set.
+void NotifyPluginOfSetCursor(OpaquePluginRef delegate,
+ const Cursor* cursor);
+
+// Returns true if the window containing the given plugin delegate is focused.
+bool GetPluginWindowHasFocus(const OpaquePluginRef delegate);
+
+} // namespace MacPluginInterpose
+
+#endif // CONTENT_PLUGIN_PLUGIN_INTERPOSE_UTIL_MAC_H_
diff --git a/content/plugin/plugin_interpose_util_mac.mm b/content/plugin/plugin_interpose_util_mac.mm
new file mode 100644
index 0000000..e1cd5bd
--- /dev/null
+++ b/content/plugin/plugin_interpose_util_mac.mm
@@ -0,0 +1,306 @@
+// Copyright (c) 2011 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/plugin/plugin_interpose_util_mac.h"
+
+#import <AppKit/AppKit.h>
+#import <objc/runtime.h>
+
+#include "content/common/plugin_messages.h"
+#include "content/plugin/plugin_thread.h"
+#include "webkit/plugins/npapi/webplugin_delegate_impl.h"
+
+namespace mac_plugin_interposing {
+
+// TODO(stuartmorgan): Make this an IPC to order the plugin process above the
+// browser process only if the browser is current frontmost.
+__attribute__((visibility("default")))
+void SwitchToPluginProcess() {
+ ProcessSerialNumber this_process, front_process;
+ if ((GetCurrentProcess(&this_process) != noErr) ||
+ (GetFrontProcess(&front_process) != noErr)) {
+ return;
+ }
+
+ Boolean matched = false;
+ if ((SameProcess(&this_process, &front_process, &matched) == noErr) &&
+ !matched) {
+ SetFrontProcess(&this_process);
+ }
+}
+
+__attribute__((visibility("default")))
+OpaquePluginRef GetActiveDelegate() {
+ return webkit::npapi::WebPluginDelegateImpl::GetActiveDelegate();
+}
+
+__attribute__((visibility("default")))
+void NotifyBrowserOfPluginSelectWindow(uint32 window_id, CGRect bounds,
+ bool modal) {
+ PluginThread* plugin_thread = PluginThread::current();
+ if (plugin_thread) {
+ gfx::Rect window_bounds(bounds);
+ plugin_thread->Send(
+ new PluginProcessHostMsg_PluginSelectWindow(window_id, window_bounds,
+ modal));
+ }
+}
+
+__attribute__((visibility("default")))
+void NotifyBrowserOfPluginShowWindow(uint32 window_id, CGRect bounds,
+ bool modal) {
+ PluginThread* plugin_thread = PluginThread::current();
+ if (plugin_thread) {
+ gfx::Rect window_bounds(bounds);
+ plugin_thread->Send(
+ new PluginProcessHostMsg_PluginShowWindow(window_id, window_bounds,
+ modal));
+ }
+}
+
+__attribute__((visibility("default")))
+void NotifyBrowserOfPluginHideWindow(uint32 window_id, CGRect bounds) {
+ PluginThread* plugin_thread = PluginThread::current();
+ if (plugin_thread) {
+ gfx::Rect window_bounds(bounds);
+ plugin_thread->Send(
+ new PluginProcessHostMsg_PluginHideWindow(window_id, window_bounds));
+ }
+}
+
+__attribute__((visibility("default")))
+void NotifyPluginOfSetThemeCursor(OpaquePluginRef delegate,
+ ThemeCursor cursor) {
+ static_cast<webkit::npapi::WebPluginDelegateImpl*>(delegate)->SetThemeCursor(
+ cursor);
+}
+
+__attribute__((visibility("default")))
+void NotifyPluginOfSetCursor(OpaquePluginRef delegate,
+ const Cursor* cursor) {
+ static_cast<webkit::npapi::WebPluginDelegateImpl*>(delegate)->SetCarbonCursor(
+ cursor);
+}
+
+void NotifyPluginOfSetCursorVisibility(bool visibility) {
+ PluginThread* plugin_thread = PluginThread::current();
+ if (plugin_thread) {
+ plugin_thread->Send(
+ new PluginProcessHostMsg_PluginSetCursorVisibility(visibility));
+ }
+}
+
+__attribute__((visibility("default")))
+bool GetPluginWindowHasFocus(const OpaquePluginRef delegate) {
+ return static_cast<webkit::npapi::WebPluginDelegateImpl*>(
+ delegate)->GetWindowHasFocus();
+}
+
+} // namespace mac_plugin_interposing
+
+#pragma mark -
+
+struct WindowInfo {
+ uint32 window_id;
+ CGRect bounds;
+ WindowInfo(NSWindow* window) {
+ NSInteger window_num = [window windowNumber];
+ window_id = window_num > 0 ? window_num : 0;
+ bounds = NSRectToCGRect([window frame]);
+ }
+};
+
+static void OnPluginWindowClosed(const WindowInfo& window_info) {
+ if (window_info.window_id == 0)
+ return;
+ mac_plugin_interposing::NotifyBrowserOfPluginHideWindow(window_info.window_id,
+ window_info.bounds);
+}
+
+static BOOL g_waiting_for_window_number = NO;
+
+static void OnPluginWindowShown(const WindowInfo& window_info, BOOL is_modal) {
+ // The window id is 0 if it has never been shown (including while it is the
+ // process of being shown for the first time); when that happens, we'll catch
+ // it in _setWindowNumber instead.
+ static BOOL s_pending_display_is_modal = NO;
+ if (window_info.window_id == 0) {
+ g_waiting_for_window_number = YES;
+ if (is_modal)
+ s_pending_display_is_modal = YES;
+ return;
+ }
+ g_waiting_for_window_number = NO;
+ if (s_pending_display_is_modal) {
+ is_modal = YES;
+ s_pending_display_is_modal = NO;
+ }
+ mac_plugin_interposing::NotifyBrowserOfPluginShowWindow(
+ window_info.window_id, window_info.bounds, is_modal);
+}
+
+@interface NSWindow (ChromePluginUtilities)
+// Returns YES if the window is visible and actually on the screen.
+- (BOOL)chromePlugin_isWindowOnScreen;
+// Returns YES if the window is the dummy window we use for popup menus;
+// see PluginInstance::PopUpContextMenu.
+// This can be removed once 10.5 is no longer supported.
+- (BOOL)chromePlugin_isPopupMenuWindow;
+@end
+
+@implementation NSWindow (ChromePluginUtilities)
+
+- (BOOL)chromePlugin_isWindowOnScreen {
+ if (![self isVisible])
+ return NO;
+ NSRect window_frame = [self frame];
+ for (NSScreen* screen in [NSScreen screens]) {
+ if (NSIntersectsRect(window_frame, [screen frame]))
+ return YES;
+ }
+ return NO;
+}
+
+- (BOOL)chromePlugin_isPopupMenuWindow {
+ return [[self title] isEqualToString:@"PopupMenuDummy"];
+}
+
+@end
+
+@interface NSWindow (ChromePluginInterposing)
+- (void)chromePlugin_orderOut:(id)sender;
+- (void)chromePlugin_orderFront:(id)sender;
+- (void)chromePlugin_makeKeyAndOrderFront:(id)sender;
+- (void)chromePlugin_setWindowNumber:(NSInteger)num;
+@end
+
+@implementation NSWindow (ChromePluginInterposing)
+
+- (void)chromePlugin_orderOut:(id)sender {
+ WindowInfo window_info(self);
+ [self chromePlugin_orderOut:sender];
+ OnPluginWindowClosed(window_info);
+}
+
+- (void)chromePlugin_orderFront:(id)sender {
+ [self chromePlugin_orderFront:sender];
+ if ([self chromePlugin_isWindowOnScreen] &&
+ ![self chromePlugin_isPopupMenuWindow])
+ mac_plugin_interposing::SwitchToPluginProcess();
+ OnPluginWindowShown(WindowInfo(self), NO);
+}
+
+- (void)chromePlugin_makeKeyAndOrderFront:(id)sender {
+ [self chromePlugin_makeKeyAndOrderFront:sender];
+ if ([self chromePlugin_isWindowOnScreen] &&
+ ![self chromePlugin_isPopupMenuWindow])
+ mac_plugin_interposing::SwitchToPluginProcess();
+ OnPluginWindowShown(WindowInfo(self), NO);
+}
+
+- (void)chromePlugin_setWindowNumber:(NSInteger)num {
+ if (!g_waiting_for_window_number || num <= 0) {
+ [self chromePlugin_setWindowNumber:num];
+ return;
+ }
+ if (![self chromePlugin_isPopupMenuWindow])
+ mac_plugin_interposing::SwitchToPluginProcess();
+ [self chromePlugin_setWindowNumber:num];
+ OnPluginWindowShown(WindowInfo(self), NO);
+}
+
+@end
+
+@interface NSApplication (ChromePluginInterposing)
+- (NSInteger)chromePlugin_runModalForWindow:(NSWindow*)window;
+@end
+
+@implementation NSApplication (ChromePluginInterposing)
+
+- (NSInteger)chromePlugin_runModalForWindow:(NSWindow*)window {
+ mac_plugin_interposing::SwitchToPluginProcess();
+ // This is out-of-order relative to the other calls, but runModalForWindow:
+ // won't return until the window closes, and the order only matters for
+ // full-screen windows.
+ OnPluginWindowShown(WindowInfo(window), YES);
+ return [self chromePlugin_runModalForWindow:window];
+}
+
+@end
+
+@interface NSCursor (ChromePluginInterposing)
+- (void)chromePlugin_set;
++ (void)chromePlugin_hide;
++ (void)chromePlugin_unhide;
+@end
+
+@implementation NSCursor (ChromePluginInterposing)
+
+- (void)chromePlugin_set {
+ OpaquePluginRef delegate = mac_plugin_interposing::GetActiveDelegate();
+ if (delegate) {
+ static_cast<webkit::npapi::WebPluginDelegateImpl*>(delegate)->SetNSCursor(
+ self);
+ return;
+ }
+ [self chromePlugin_set];
+}
+
++ (void)chromePlugin_hide {
+ mac_plugin_interposing::NotifyPluginOfSetCursorVisibility(false);
+}
+
++ (void)chromePlugin_unhide {
+ mac_plugin_interposing::NotifyPluginOfSetCursorVisibility(true);
+}
+
+@end
+
+#pragma mark -
+
+static void ExchangeMethods(Class target_class,
+ BOOL class_method,
+ SEL original,
+ SEL replacement) {
+ Method m1;
+ Method m2;
+ if (class_method) {
+ m1 = class_getClassMethod(target_class, original);
+ m2 = class_getClassMethod(target_class, replacement);
+ } else {
+ m1 = class_getInstanceMethod(target_class, original);
+ m2 = class_getInstanceMethod(target_class, replacement);
+ }
+ if (m1 && m2)
+ method_exchangeImplementations(m1, m2);
+ else
+ NOTREACHED() << "Cocoa swizzling failed";
+}
+
+namespace mac_plugin_interposing {
+
+void SetUpCocoaInterposing() {
+ Class nswindow_class = [NSWindow class];
+ ExchangeMethods(nswindow_class, NO, @selector(orderOut:),
+ @selector(chromePlugin_orderOut:));
+ ExchangeMethods(nswindow_class, NO, @selector(orderFront:),
+ @selector(chromePlugin_orderFront:));
+ ExchangeMethods(nswindow_class, NO, @selector(makeKeyAndOrderFront:),
+ @selector(chromePlugin_makeKeyAndOrderFront:));
+ ExchangeMethods(nswindow_class, NO, @selector(_setWindowNumber:),
+ @selector(chromePlugin_setWindowNumber:));
+
+ ExchangeMethods([NSApplication class], NO, @selector(runModalForWindow:),
+ @selector(chromePlugin_runModalForWindow:));
+
+ Class nscursor_class = [NSCursor class];
+ ExchangeMethods(nscursor_class, NO, @selector(set),
+ @selector(chromePlugin_set));
+ ExchangeMethods(nscursor_class, YES, @selector(hide),
+ @selector(chromePlugin_hide));
+ ExchangeMethods(nscursor_class, YES, @selector(unhide),
+ @selector(chromePlugin_unhide));
+}
+
+} // namespace mac_plugin_interposing
diff --git a/content/plugin/plugin_main.cc b/content/plugin/plugin_main.cc
new file mode 100644
index 0000000..fef3e95
--- /dev/null
+++ b/content/plugin/plugin_main.cc
@@ -0,0 +1,171 @@
+// Copyright (c) 2010 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 "build/build_config.h"
+
+#if defined(OS_WIN)
+#include <objbase.h>
+#include <windows.h>
+#endif
+
+#include "base/command_line.h"
+#include "base/message_loop.h"
+#include "base/string_util.h"
+#include "base/threading/platform_thread.h"
+#include "content/common/child_process.h"
+#include "content/common/content_switches.h"
+#include "content/common/hi_res_timer_manager.h"
+#include "content/common/main_function_params.h"
+#include "content/plugin/plugin_thread.h"
+#include "ui/base/system_monitor/system_monitor.h"
+
+#if defined(OS_WIN)
+#include "content/common/injection_test_dll.h"
+#include "sandbox/src/sandbox.h"
+#elif defined(OS_POSIX) && !defined(OS_MACOSX)
+#include "base/global_descriptors_posix.h"
+#include "ipc/ipc_descriptors.h"
+#endif
+
+#if defined(OS_MACOSX)
+// Removes our Carbon library interposing from the environment so that it
+// doesn't carry into any processes that plugins might start.
+void TrimInterposeEnvironment();
+
+// Initializes the global Cocoa application object.
+void InitializeChromeApplication();
+#elif defined(OS_LINUX)
+// Work around an unimplemented instruction in 64-bit Flash.
+void WorkaroundFlashLAHF();
+#endif
+
+#if defined(OS_WIN)
+// This function is provided so that the built-in flash can lock down the
+// sandbox by calling DelayedLowerToken(0).
+extern "C" DWORD __declspec(dllexport) __stdcall DelayedLowerToken(void* ts) {
+ // s_ts is only set the first time the function is called, which happens
+ // in PluginMain.
+ static sandbox::TargetServices* s_ts =
+ reinterpret_cast<sandbox::TargetServices*>(ts);
+ if (ts)
+ return 0;
+ s_ts->LowerToken();
+ return 1;
+};
+
+// Returns true if the plugin to be loaded is the internal flash.
+bool IsPluginBuiltInFlash(const CommandLine& cmd_line) {
+ FilePath path = cmd_line.GetSwitchValuePath(switches::kPluginPath);
+ return (path.BaseName() == FilePath(L"gcswf32.dll"));
+}
+
+// Before we lock down the flash sandbox, we need to activate
+// the IME machinery. After lock down it seems it is unable
+// to start. Note that we leak the IME context on purpose.
+int PreloadIMEForFlash() {
+ HIMC imc = ::ImmCreateContext();
+ if (!imc)
+ return 0;
+ if (::ImmGetOpenStatus(imc))
+ return 1;
+ return 2;
+}
+
+#endif
+
+// main() routine for running as the plugin process.
+int PluginMain(const MainFunctionParams& parameters) {
+ // The main thread of the plugin services UI.
+#if defined(OS_MACOSX)
+#if !defined(__LP64__)
+ TrimInterposeEnvironment();
+#endif
+ InitializeChromeApplication();
+#endif
+ MessageLoop main_message_loop(MessageLoop::TYPE_UI);
+ base::PlatformThread::SetName("CrPluginMain");
+
+ ui::SystemMonitor system_monitor;
+ HighResolutionTimerManager high_resolution_timer_manager;
+
+ const CommandLine& parsed_command_line = parameters.command_line_;
+
+#if defined(OS_LINUX)
+
+#if defined(ARCH_CPU_64_BITS)
+ WorkaroundFlashLAHF();
+#endif
+
+#elif defined(OS_WIN)
+ sandbox::TargetServices* target_services =
+ parameters.sandbox_info_.TargetServices();
+
+ CoInitialize(NULL);
+ DVLOG(1) << "Started plugin with "
+ << parsed_command_line.command_line_string();
+
+ HMODULE sandbox_test_module = NULL;
+ bool no_sandbox = parsed_command_line.HasSwitch(switches::kNoSandbox);
+
+ if (target_services && !no_sandbox) {
+ // The command line might specify a test plugin to load.
+ if (parsed_command_line.HasSwitch(switches::kTestSandbox)) {
+ std::wstring test_plugin_name =
+ parsed_command_line.GetSwitchValueNative(switches::kTestSandbox);
+ sandbox_test_module = LoadLibrary(test_plugin_name.c_str());
+ DCHECK(sandbox_test_module);
+ }
+ }
+#endif
+ if (parsed_command_line.HasSwitch(switches::kPluginStartupDialog)) {
+ ChildProcess::WaitForDebugger("Plugin");
+ }
+
+ {
+ ChildProcess plugin_process;
+ plugin_process.set_main_thread(new PluginThread());
+#if defined(OS_WIN)
+ if (!no_sandbox && target_services) {
+ // We are sandboxing the plugin. If it is a generic plug-in, we lock down
+ // the sandbox right away, but if it is the built-in flash we let flash
+ // start elevated and it will call DelayedLowerToken(0) when it's ready.
+ if (IsPluginBuiltInFlash(parsed_command_line)) {
+ DVLOG(1) << "Sandboxing flash";
+ if (!PreloadIMEForFlash())
+ DVLOG(1) << "IME preload failed";
+ DelayedLowerToken(target_services);
+ } else {
+ target_services->LowerToken();
+ }
+ }
+ if (sandbox_test_module) {
+ RunPluginTests run_security_tests =
+ reinterpret_cast<RunPluginTests>(GetProcAddress(sandbox_test_module,
+ kPluginTestCall));
+ DCHECK(run_security_tests);
+ if (run_security_tests) {
+ int test_count = 0;
+ DVLOG(1) << "Running plugin security tests";
+ BOOL result = run_security_tests(&test_count);
+ DCHECK(result) << "Test number " << test_count << " has failed.";
+ // If we are in release mode, crash or debug the process.
+ if (!result) {
+ __debugbreak();
+ _exit(1);
+ }
+ }
+
+ FreeLibrary(sandbox_test_module);
+ }
+#endif
+
+ MessageLoop::current()->Run();
+ }
+
+#if defined(OS_WIN)
+ CoUninitialize();
+#endif
+
+ return 0;
+}
diff --git a/content/plugin/plugin_main_linux.cc b/content/plugin/plugin_main_linux.cc
new file mode 100644
index 0000000..6bb9da7
--- /dev/null
+++ b/content/plugin/plugin_main_linux.cc
@@ -0,0 +1,72 @@
+// Copyright (c) 2009 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 <signal.h>
+#include <string.h>
+#include <sys/types.h>
+#include <syscall.h>
+#include <unistd.h>
+
+#include "build/build_config.h"
+
+// This whole file is only useful on 64-bit architectures.
+#if defined(ARCH_CPU_64_BITS)
+
+namespace {
+
+// Signal handler for SIGILL; see WorkaroundFlashLAHF().
+void SignalHandler(int signum, siginfo_t* info, void* void_context) {
+ const char kLAHFInstruction = 0x9f;
+ ucontext_t* context = static_cast<ucontext_t*>(void_context);
+ greg_t* regs = context->uc_mcontext.gregs;
+ char instruction = *reinterpret_cast<char*>(regs[REG_RIP]);
+
+ // Check whether this is the kind of SIGILL we care about.
+ // (info->si_addr can be NULL when we get a SIGILL via other means,
+ // like with kill.)
+ if (signum != SIGILL || instruction != kLAHFInstruction) {
+ // Not the problem we're interested in. Reraise the signal. We
+ // need to be careful to handle threads etc. properly.
+
+ struct sigaction sa;
+ sa.sa_flags = 0;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_handler = SIG_DFL;
+ sigaction(signum, &sa, NULL);
+
+ // block the current signal
+ sigset_t block_set;
+ sigemptyset(&block_set);
+ sigaddset(&block_set, signum);
+ sigprocmask(SIG_BLOCK, &block_set, NULL);
+
+ // Re-raise signal. It won't be delivered until we return.
+ syscall(SYS_tkill, syscall(SYS_gettid), signum);
+ return;
+ }
+
+ // LAHF moves the low byte of the EFLAGS register to AH. Emulate that.
+ reinterpret_cast<char*>(&regs[REG_RAX])[1] =
+ reinterpret_cast<char*>(&regs[REG_EFL])[0];
+ // And advance the instruction pointer past the (one-byte) instruction.
+ ++regs[REG_RIP];
+}
+
+} // namespace
+
+// 64-bit Flash sometimes uses the LAHF instruction which isn't
+// available on some CPUs. We can work around it by catching SIGILL
+// (illegal instruction), checking if the signal was caused by this
+// particular circumstance, emulating the instruction, and resuming.
+// This function registers the signal handler.
+void WorkaroundFlashLAHF() {
+ struct sigaction action;
+ memset(&action, 0, sizeof(action));
+ action.sa_flags = SA_SIGINFO;
+ action.sa_sigaction = &SignalHandler;
+
+ sigaction(SIGILL, &action, NULL);
+}
+
+#endif // defined(ARCH_CPU_64_BITS)
diff --git a/content/plugin/plugin_main_mac.mm b/content/plugin/plugin_main_mac.mm
new file mode 100644
index 0000000..15f230c
--- /dev/null
+++ b/content/plugin/plugin_main_mac.mm
@@ -0,0 +1,50 @@
+// Copyright (c) 2010 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 "base/environment.h"
+#include "base/scoped_ptr.h"
+#include "base/string_util.h"
+#include "content/common/chrome_application_mac.h"
+#include "content/common/plugin_carbon_interpose_constants_mac.h"
+#include "content/plugin/plugin_interpose_util_mac.h"
+
+#if !defined(__LP64__)
+void TrimInterposeEnvironment() {
+ scoped_ptr<base::Environment> env(base::Environment::Create());
+
+ std::string interpose_list;
+ if (!env->GetVar(plugin_interpose_strings::kDYLDInsertLibrariesKey,
+ &interpose_list)) {
+ NOTREACHED() << "No interposing libraries set";
+ return;
+ }
+
+ // The list is a :-separated list of paths. Because we append our interpose
+ // library just before forking in plugin_process_host.cc, the only cases we
+ // need to handle are:
+ // 1) The whole string is "<kInterposeLibraryPath>", so just clear it, or
+ // 2) ":<kInterposeLibraryPath>" is the end of the string, so trim and re-set.
+ std::string interpose_library_path(
+ plugin_interpose_strings::kInterposeLibraryPath);
+ DCHECK_GE(interpose_list.size(), interpose_library_path.size());
+ size_t suffix_offset = interpose_list.size() - interpose_library_path.size();
+ if (suffix_offset == 0 &&
+ interpose_list == interpose_library_path) {
+ env->UnSetVar(plugin_interpose_strings::kDYLDInsertLibrariesKey);
+ } else if (suffix_offset > 0 && interpose_list[suffix_offset - 1] == ':' &&
+ interpose_list.substr(suffix_offset) == interpose_library_path) {
+ std::string trimmed_list = interpose_list.substr(0, suffix_offset - 1);
+ env->SetVar(plugin_interpose_strings::kDYLDInsertLibrariesKey,
+ trimmed_list.c_str());
+ } else {
+ NOTREACHED() << "Missing Carbon interposing library";
+ }
+}
+#endif
+
+void InitializeChromeApplication() {
+ [CrApplication sharedApplication];
+
+ mac_plugin_interposing::SetUpCocoaInterposing();
+}
diff --git a/content/plugin/plugin_thread.cc b/content/plugin/plugin_thread.cc
new file mode 100644
index 0000000..c2ca935
--- /dev/null
+++ b/content/plugin/plugin_thread.cc
@@ -0,0 +1,193 @@
+// Copyright (c) 2010 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/plugin/plugin_thread.h"
+
+#include "build/build_config.h"
+
+#if defined(USE_X11)
+#include <gtk/gtk.h>
+#elif defined(OS_MACOSX)
+#include <CoreFoundation/CoreFoundation.h>
+#endif
+
+#include <string>
+#include <vector>
+
+#include "base/command_line.h"
+#include "base/lazy_instance.h"
+#include "base/process_util.h"
+#include "base/threading/thread_local.h"
+#include "content/common/child_process.h"
+#include "content/common/content_client.h"
+#include "content/common/content_switches.h"
+#include "content/common/child_process_messages.h"
+#include "content/common/plugin_messages.h"
+#include "content/plugin/npobject_util.h"
+#include "ipc/ipc_channel_handle.h"
+#include "net/base/net_errors.h"
+#include "webkit/glue/webkit_glue.h"
+#include "webkit/plugins/npapi/plugin_lib.h"
+#include "webkit/plugins/npapi/webplugin_delegate_impl.h"
+
+#if defined(TOOLKIT_USES_GTK)
+#include "ui/gfx/gtk_util.h"
+#endif
+
+#if defined(USE_X11)
+#include "ui/base/x/x11_util.h"
+#endif
+
+static base::LazyInstance<base::ThreadLocalPointer<PluginThread> > lazy_tls(
+ base::LINKER_INITIALIZED);
+
+PluginThread::PluginThread()
+ : preloaded_plugin_module_(NULL) {
+ plugin_path_ =
+ CommandLine::ForCurrentProcess()->GetSwitchValuePath(
+ switches::kPluginPath);
+
+ lazy_tls.Pointer()->Set(this);
+#if defined(OS_LINUX)
+ {
+ // XEmbed plugins assume they are hosted in a Gtk application, so we need
+ // to initialize Gtk in the plugin process.
+ g_thread_init(NULL);
+
+ // Flash has problems receiving clicks with newer GTKs due to the
+ // client-side windows change. To be safe, we just always set the
+ // backwards-compat environment variable.
+ setenv("GDK_NATIVE_WINDOWS", "1", 1);
+
+ gfx::GtkInitFromCommandLine(*CommandLine::ForCurrentProcess());
+
+ // GTK after 2.18 resets the environment variable. But if we're using
+ // nspluginwrapper, that means it'll spawn its subprocess without the
+ // environment variable! So set it again.
+ setenv("GDK_NATIVE_WINDOWS", "1", 1);
+ }
+
+ ui::SetDefaultX11ErrorHandlers();
+#endif
+
+ PatchNPNFunctions();
+
+ // Preload the library to avoid loading, unloading then reloading
+ preloaded_plugin_module_ = base::LoadNativeLibrary(plugin_path_);
+
+ scoped_refptr<webkit::npapi::PluginLib> plugin(
+ webkit::npapi::PluginLib::CreatePluginLib(plugin_path_));
+ if (plugin.get())
+ plugin->NP_Initialize();
+
+ content::GetContentClient()->PluginProcessStarted(
+ plugin.get() ? plugin->plugin_info().name : string16());
+
+ // Certain plugins, such as flash, steal the unhandled exception filter
+ // thus we never get crash reports when they fault. This call fixes it.
+ message_loop()->set_exception_restoration(true);
+}
+
+PluginThread::~PluginThread() {
+ if (preloaded_plugin_module_) {
+ base::UnloadNativeLibrary(preloaded_plugin_module_);
+ preloaded_plugin_module_ = NULL;
+ }
+ PluginChannelBase::CleanupChannels();
+ webkit::npapi::PluginLib::UnloadAllPlugins();
+
+ if (webkit_glue::ShouldForcefullyTerminatePluginProcess())
+ base::KillProcess(base::GetCurrentProcessHandle(), 0, /* wait= */ false);
+
+ lazy_tls.Pointer()->Set(NULL);
+}
+
+PluginThread* PluginThread::current() {
+ return lazy_tls.Pointer()->Get();
+}
+
+bool PluginThread::OnControlMessageReceived(const IPC::Message& msg) {
+ bool handled = true;
+ IPC_BEGIN_MESSAGE_MAP(PluginThread, msg)
+ IPC_MESSAGE_HANDLER(PluginProcessMsg_CreateChannel, OnCreateChannel)
+ IPC_MESSAGE_HANDLER(PluginProcessMsg_NotifyRenderersOfPendingShutdown,
+ OnNotifyRenderersOfPendingShutdown)
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP()
+ return handled;
+}
+
+void PluginThread::OnCreateChannel(int renderer_id,
+ bool incognito) {
+ scoped_refptr<PluginChannel> channel(PluginChannel::GetPluginChannel(
+ renderer_id, ChildProcess::current()->io_message_loop()));
+ IPC::ChannelHandle channel_handle;
+ if (channel.get()) {
+ channel_handle.name = channel->channel_handle().name;
+#if defined(OS_POSIX)
+ // On POSIX, pass the renderer-side FD.
+ channel_handle.socket = base::FileDescriptor(channel->renderer_fd(), false);
+#endif
+ channel->set_incognito(incognito);
+ }
+
+ Send(new PluginProcessHostMsg_ChannelCreated(channel_handle));
+}
+
+void PluginThread::OnNotifyRenderersOfPendingShutdown() {
+ PluginChannel::NotifyRenderersOfPendingShutdown();
+}
+
+namespace webkit_glue {
+
+#if defined(OS_WIN)
+bool DownloadUrl(const std::string& url, HWND caller_window) {
+ PluginThread* plugin_thread = PluginThread::current();
+ if (!plugin_thread) {
+ return false;
+ }
+
+ IPC::Message* message =
+ new PluginProcessHostMsg_DownloadUrl(MSG_ROUTING_NONE, url,
+ ::GetCurrentProcessId(),
+ caller_window);
+ return plugin_thread->Send(message);
+}
+#endif
+
+bool GetPluginFinderURL(std::string* plugin_finder_url) {
+ if (!plugin_finder_url) {
+ NOTREACHED();
+ return false;
+ }
+
+ PluginThread* plugin_thread = PluginThread::current();
+ if (!plugin_thread)
+ return false;
+
+ plugin_thread->Send(
+ new PluginProcessHostMsg_GetPluginFinderUrl(plugin_finder_url));
+ // If we get an empty string back this means the plugin finder has been
+ // disabled.
+ return true;
+}
+
+bool IsDefaultPluginEnabled() {
+ return true;
+}
+
+bool FindProxyForUrl(const GURL& url, std::string* proxy_list) {
+ int net_error;
+ std::string proxy_result;
+
+ bool result = ChildThread::current()->Send(
+ new ChildProcessHostMsg_ResolveProxy(url, &net_error, &proxy_result));
+ if (!result || net_error != net::OK)
+ return false;
+
+ *proxy_list = proxy_result;
+ return true;
+}
+
+} // namespace webkit_glue
diff --git a/content/plugin/plugin_thread.h b/content/plugin/plugin_thread.h
new file mode 100644
index 0000000..efd96b6
--- /dev/null
+++ b/content/plugin/plugin_thread.h
@@ -0,0 +1,54 @@
+// Copyright (c) 2010 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.
+
+#ifndef CONTENT_PLUGIN_PLUGIN_THREAD_H_
+#define CONTENT_PLUGIN_PLUGIN_THREAD_H_
+#pragma once
+
+#include "base/file_path.h"
+#include "base/native_library.h"
+#include "build/build_config.h"
+#include "content/common/child_thread.h"
+#include "content/plugin/plugin_channel.h"
+#include "webkit/plugins/npapi/plugin_lib.h"
+
+#if defined(OS_POSIX)
+#include "base/file_descriptor_posix.h"
+#endif
+
+// The PluginThread class represents a background thread where plugin instances
+// live. Communication occurs between WebPluginDelegateProxy in the renderer
+// process and WebPluginDelegateStub in this thread through IPC messages.
+class PluginThread : public ChildThread {
+ public:
+ PluginThread();
+ ~PluginThread();
+
+ // Returns the one plugin thread.
+ static PluginThread* current();
+
+ FilePath plugin_path() { return plugin_path_; }
+
+ private:
+ virtual bool OnControlMessageReceived(const IPC::Message& msg);
+
+ // Callback for when a channel has been created.
+ void OnCreateChannel(int renderer_id, bool incognito);
+ void OnPluginMessage(const std::vector<uint8> &data);
+ void OnNotifyRenderersOfPendingShutdown();
+#if defined(OS_MACOSX)
+ void OnAppActivated();
+ void OnPluginFocusNotify(uint32 instance_id);
+#endif
+
+ // The plugin module which is preloaded in Init
+ base::NativeLibrary preloaded_plugin_module_;
+
+ // Points to the plugin file that this process hosts.
+ FilePath plugin_path_;
+
+ DISALLOW_COPY_AND_ASSIGN(PluginThread);
+};
+
+#endif // CONTENT_PLUGIN_PLUGIN_THREAD_H_
diff --git a/content/plugin/webplugin_accelerated_surface_proxy_mac.cc b/content/plugin/webplugin_accelerated_surface_proxy_mac.cc
new file mode 100644
index 0000000..7f5d486
--- /dev/null
+++ b/content/plugin/webplugin_accelerated_surface_proxy_mac.cc
@@ -0,0 +1,79 @@
+// Copyright (c) 2010 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.
+
+#import <OpenGL/OpenGL.h>
+
+#include "content/plugin/webplugin_accelerated_surface_proxy_mac.h"
+
+#include "app/surface/accelerated_surface_mac.h"
+#include "app/surface/transport_dib.h"
+#include "content/plugin/webplugin_proxy.h"
+
+WebPluginAcceleratedSurfaceProxy::WebPluginAcceleratedSurfaceProxy(
+ WebPluginProxy* plugin_proxy)
+ : plugin_proxy_(plugin_proxy),
+ window_handle_(NULL) {
+ surface_ = new AcceleratedSurface;
+ // It's possible for OpenGL to fail to initialze (e.g., if an incompatible
+ // mode is forced via flags), so handle that gracefully.
+ if (!surface_->Initialize(NULL, true)) {
+ delete surface_;
+ surface_ = NULL;
+ return;
+ }
+
+ // Only used for 10.5 support, but harmless on 10.6+.
+ surface_->SetTransportDIBAllocAndFree(
+ NewCallback(plugin_proxy_, &WebPluginProxy::AllocSurfaceDIB),
+ NewCallback(plugin_proxy_, &WebPluginProxy::FreeSurfaceDIB));
+}
+
+WebPluginAcceleratedSurfaceProxy::~WebPluginAcceleratedSurfaceProxy() {
+ if (surface_) {
+ surface_->Destroy();
+ delete surface_;
+ surface_ = NULL;
+ }
+}
+
+void WebPluginAcceleratedSurfaceProxy::SetWindowHandle(
+ gfx::PluginWindowHandle window) {
+ window_handle_ = window;
+}
+
+void WebPluginAcceleratedSurfaceProxy::SetSize(const gfx::Size& size) {
+ if (!surface_)
+ return;
+
+ uint64 io_surface_id = surface_->SetSurfaceSize(size);
+ if (io_surface_id) {
+ plugin_proxy_->SetAcceleratedSurface(window_handle_, size, io_surface_id);
+ } else {
+ TransportDIB::Handle transport_dib = surface_->SetTransportDIBSize(size);
+ if (TransportDIB::is_valid(transport_dib)) {
+ plugin_proxy_->SetAcceleratedDIB(window_handle_, size, transport_dib);
+ }
+ }
+}
+
+CGLContextObj WebPluginAcceleratedSurfaceProxy::context() {
+ return surface_ ? surface_->context() : NULL;
+}
+
+void WebPluginAcceleratedSurfaceProxy::StartDrawing() {
+ if (!surface_)
+ return;
+
+ surface_->MakeCurrent();
+ surface_->Clear(gfx::Rect(surface_->GetSize()));
+}
+
+void WebPluginAcceleratedSurfaceProxy::EndDrawing() {
+ if (!surface_)
+ return;
+
+ surface_->SwapBuffers();
+ plugin_proxy_->AcceleratedFrameBuffersDidSwap(
+ window_handle_, surface_->GetSurfaceId());
+}
diff --git a/content/plugin/webplugin_accelerated_surface_proxy_mac.h b/content/plugin/webplugin_accelerated_surface_proxy_mac.h
new file mode 100644
index 0000000..b41239f
--- /dev/null
+++ b/content/plugin/webplugin_accelerated_surface_proxy_mac.h
@@ -0,0 +1,39 @@
+// Copyright (c) 2010 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.
+
+#ifndef CONTENT_PLUGIN_WEBPLUGIN_ACCELERATED_SURFACE_PROXY_H_
+#define CONTENT_PLUGIN_WEBPLUGIN_ACCELERATED_SURFACE_PROXY_H_
+#pragma once
+
+#include "webkit/plugins/npapi/webplugin_accelerated_surface_mac.h"
+
+class WebPluginProxy;
+class AcceleratedSurface;
+
+// Out-of-process implementation of WebPluginAcceleratedSurface that proxies
+// calls through a WebPluginProxy.
+class WebPluginAcceleratedSurfaceProxy
+ : public webkit::npapi::WebPluginAcceleratedSurface {
+ public:
+ // Creates a new WebPluginAcceleratedSurfaceProxy that uses plugin_proxy
+ // to proxy calls. plugin_proxy must outlive this object.
+ WebPluginAcceleratedSurfaceProxy(WebPluginProxy* plugin_proxy);
+ virtual ~WebPluginAcceleratedSurfaceProxy();
+
+ // WebPluginAcceleratedSurface implementation.
+ virtual void SetWindowHandle(gfx::PluginWindowHandle window);
+ virtual void SetSize(const gfx::Size& size);
+ virtual CGLContextObj context();
+ virtual void StartDrawing();
+ virtual void EndDrawing();
+
+ private:
+ WebPluginProxy* plugin_proxy_; // Weak ref.
+ gfx::PluginWindowHandle window_handle_;
+ AcceleratedSurface* surface_;
+
+ DISALLOW_COPY_AND_ASSIGN(WebPluginAcceleratedSurfaceProxy);
+};
+
+#endif // CONTENT_PLUGIN_WEBPLUGIN_ACCELERATED_SURFACE_PROXY_H_
diff --git a/content/plugin/webplugin_delegate_stub.cc b/content/plugin/webplugin_delegate_stub.cc
new file mode 100644
index 0000000..cf26fc0
--- /dev/null
+++ b/content/plugin/webplugin_delegate_stub.cc
@@ -0,0 +1,451 @@
+// Copyright (c) 2011 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/plugin/webplugin_delegate_stub.h"
+
+#include "build/build_config.h"
+
+#include "base/command_line.h"
+#include "content/common/content_client.h"
+#include "content/common/content_switches.h"
+#include "content/common/plugin_messages.h"
+#include "content/plugin/npobject_stub.h"
+#include "content/plugin/plugin_channel.h"
+#include "content/plugin/plugin_thread.h"
+#include "content/plugin/webplugin_proxy.h"
+#include "third_party/npapi/bindings/npapi.h"
+#include "third_party/npapi/bindings/npruntime.h"
+#include "skia/ext/platform_device.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebBindings.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebCursorInfo.h"
+#include "webkit/plugins/npapi/webplugin_delegate_impl.h"
+#include "webkit/glue/webcursor.h"
+
+#if defined(OS_WIN)
+#include "base/scoped_ptr.h"
+#include "printing/native_metafile_factory.h"
+#include "printing/native_metafile.h"
+#endif // defined(OS_WIN)
+
+using WebKit::WebBindings;
+using WebKit::WebCursorInfo;
+using webkit::npapi::WebPlugin;
+using webkit::npapi::WebPluginResourceClient;
+
+class FinishDestructionTask : public Task {
+ public:
+ FinishDestructionTask(webkit::npapi::WebPluginDelegateImpl* delegate,
+ WebPlugin* webplugin)
+ : delegate_(delegate), webplugin_(webplugin) {
+ }
+
+ void Run() {
+ // WebPlugin must outlive WebPluginDelegate.
+ if (delegate_)
+ delegate_->PluginDestroyed();
+
+ delete webplugin_;
+ }
+
+ private:
+ webkit::npapi::WebPluginDelegateImpl* delegate_;
+ webkit::npapi::WebPlugin* webplugin_;
+};
+
+WebPluginDelegateStub::WebPluginDelegateStub(
+ const std::string& mime_type, int instance_id, PluginChannel* channel) :
+ mime_type_(mime_type),
+ instance_id_(instance_id),
+ channel_(channel),
+ delegate_(NULL),
+ webplugin_(NULL),
+ in_destructor_(false) {
+ DCHECK(channel);
+}
+
+WebPluginDelegateStub::~WebPluginDelegateStub() {
+ in_destructor_ = true;
+ content::GetContentClient()->SetActiveURL(page_url_);
+
+ if (channel_->in_send()) {
+ // The delegate or an npobject is in the callstack, so don't delete it
+ // right away.
+ MessageLoop::current()->PostNonNestableTask(FROM_HERE,
+ new FinishDestructionTask(delegate_, webplugin_));
+ } else {
+ // Safe to delete right away.
+ if (delegate_)
+ delegate_->PluginDestroyed();
+
+ delete webplugin_;
+ }
+}
+
+bool WebPluginDelegateStub::OnMessageReceived(const IPC::Message& msg) {
+ content::GetContentClient()->SetActiveURL(page_url_);
+
+ // A plugin can execute a script to delete itself in any of its NPP methods.
+ // Hold an extra reference to ourself so that if this does occur and we're
+ // handling a sync message, we don't crash when attempting to send a reply.
+ // The exception to this is when we're already in the destructor.
+ if (!in_destructor_)
+ AddRef();
+
+ bool handled = true;
+ IPC_BEGIN_MESSAGE_MAP(WebPluginDelegateStub, msg)
+ IPC_MESSAGE_HANDLER(PluginMsg_Init, OnInit)
+ IPC_MESSAGE_HANDLER(PluginMsg_WillSendRequest, OnWillSendRequest)
+ IPC_MESSAGE_HANDLER(PluginMsg_DidReceiveResponse, OnDidReceiveResponse)
+ IPC_MESSAGE_HANDLER(PluginMsg_DidReceiveData, OnDidReceiveData)
+ IPC_MESSAGE_HANDLER(PluginMsg_DidFinishLoading, OnDidFinishLoading)
+ IPC_MESSAGE_HANDLER(PluginMsg_DidFail, OnDidFail)
+ IPC_MESSAGE_HANDLER(PluginMsg_DidFinishLoadWithReason,
+ OnDidFinishLoadWithReason)
+ IPC_MESSAGE_HANDLER(PluginMsg_SetFocus, OnSetFocus)
+ IPC_MESSAGE_HANDLER(PluginMsg_HandleInputEvent, OnHandleInputEvent)
+ IPC_MESSAGE_HANDLER(PluginMsg_Paint, OnPaint)
+ IPC_MESSAGE_HANDLER(PluginMsg_DidPaint, OnDidPaint)
+ IPC_MESSAGE_HANDLER(PluginMsg_Print, OnPrint)
+ IPC_MESSAGE_HANDLER(PluginMsg_GetPluginScriptableObject,
+ OnGetPluginScriptableObject)
+ IPC_MESSAGE_HANDLER(PluginMsg_UpdateGeometry, OnUpdateGeometry)
+ IPC_MESSAGE_HANDLER(PluginMsg_UpdateGeometrySync, OnUpdateGeometry)
+ IPC_MESSAGE_HANDLER(PluginMsg_SendJavaScriptStream,
+ OnSendJavaScriptStream)
+ IPC_MESSAGE_HANDLER(PluginMsg_SetContentAreaFocus, OnSetContentAreaFocus)
+#if defined(OS_MACOSX)
+ IPC_MESSAGE_HANDLER(PluginMsg_SetWindowFocus, OnSetWindowFocus)
+ IPC_MESSAGE_HANDLER(PluginMsg_ContainerHidden, OnContainerHidden)
+ IPC_MESSAGE_HANDLER(PluginMsg_ContainerShown, OnContainerShown)
+ IPC_MESSAGE_HANDLER(PluginMsg_WindowFrameChanged, OnWindowFrameChanged)
+ IPC_MESSAGE_HANDLER(PluginMsg_ImeCompositionCompleted,
+ OnImeCompositionCompleted)
+#endif
+ IPC_MESSAGE_HANDLER(PluginMsg_DidReceiveManualResponse,
+ OnDidReceiveManualResponse)
+ IPC_MESSAGE_HANDLER(PluginMsg_DidReceiveManualData, OnDidReceiveManualData)
+ IPC_MESSAGE_HANDLER(PluginMsg_DidFinishManualLoading,
+ OnDidFinishManualLoading)
+ IPC_MESSAGE_HANDLER(PluginMsg_DidManualLoadFail, OnDidManualLoadFail)
+ IPC_MESSAGE_HANDLER(PluginMsg_InstallMissingPlugin, OnInstallMissingPlugin)
+ IPC_MESSAGE_HANDLER(PluginMsg_HandleURLRequestReply,
+ OnHandleURLRequestReply)
+ IPC_MESSAGE_HANDLER(PluginMsg_HTTPRangeRequestReply,
+ OnHTTPRangeRequestReply)
+#if defined(OS_MACOSX)
+ IPC_MESSAGE_HANDLER(PluginMsg_SetFakeAcceleratedSurfaceWindowHandle,
+ OnSetFakeAcceleratedSurfaceWindowHandle)
+#endif
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP()
+
+ if (!in_destructor_)
+ Release();
+
+ DCHECK(handled);
+ return handled;
+}
+
+bool WebPluginDelegateStub::Send(IPC::Message* msg) {
+ return channel_->Send(msg);
+}
+
+void WebPluginDelegateStub::OnInit(const PluginMsg_Init_Params& params,
+ bool* result) {
+ page_url_ = params.page_url;
+ content::GetContentClient()->SetActiveURL(page_url_);
+
+ *result = false;
+ if (params.arg_names.size() != params.arg_values.size()) {
+ NOTREACHED();
+ return;
+ }
+
+ const CommandLine& command_line = *CommandLine::ForCurrentProcess();
+ FilePath path =
+ command_line.GetSwitchValuePath(switches::kPluginPath);
+
+ gfx::PluginWindowHandle parent = gfx::kNullPluginWindow;
+#if defined(OS_WIN)
+ parent = gfx::NativeViewFromId(params.containing_window);
+#elif defined(OS_LINUX)
+ // This code is disabled, See issue 17110.
+ // The problem is that the XID can change at arbitrary times (e.g. when the
+ // tab is detached then reattached), so we need to be able to track these
+ // changes, and let the PluginInstance know.
+ // PluginThread::current()->Send(new PluginProcessHostMsg_MapNativeViewId(
+ // params.containing_window, &parent));
+#endif
+
+ webplugin_ = new WebPluginProxy(
+ channel_, instance_id_, page_url_, params.containing_window,
+ params.host_render_view_routing_id);
+ delegate_ = webkit::npapi::WebPluginDelegateImpl::Create(
+ path, mime_type_, parent);
+ if (delegate_) {
+ webplugin_->set_delegate(delegate_);
+ *result = delegate_->Initialize(params.url,
+ params.arg_names,
+ params.arg_values,
+ webplugin_,
+ params.load_manually);
+ }
+}
+
+void WebPluginDelegateStub::OnWillSendRequest(int id, const GURL& url,
+ int http_status_code) {
+ WebPluginResourceClient* client = webplugin_->GetResourceClient(id);
+ if (!client)
+ return;
+
+ client->WillSendRequest(url, http_status_code);
+}
+
+void WebPluginDelegateStub::OnDidReceiveResponse(
+ const PluginMsg_DidReceiveResponseParams& params) {
+ WebPluginResourceClient* client = webplugin_->GetResourceClient(params.id);
+ if (!client)
+ return;
+
+ client->DidReceiveResponse(params.mime_type,
+ params.headers,
+ params.expected_length,
+ params.last_modified,
+ params.request_is_seekable);
+}
+
+void WebPluginDelegateStub::OnDidReceiveData(int id,
+ const std::vector<char>& buffer,
+ int data_offset) {
+ WebPluginResourceClient* client = webplugin_->GetResourceClient(id);
+ if (!client)
+ return;
+
+ client->DidReceiveData(&buffer.front(), static_cast<int>(buffer.size()),
+ data_offset);
+}
+
+void WebPluginDelegateStub::OnDidFinishLoading(int id) {
+ WebPluginResourceClient* client = webplugin_->GetResourceClient(id);
+ if (!client)
+ return;
+
+ client->DidFinishLoading();
+}
+
+void WebPluginDelegateStub::OnDidFail(int id) {
+ WebPluginResourceClient* client = webplugin_->GetResourceClient(id);
+ if (!client)
+ return;
+
+ client->DidFail();
+}
+
+void WebPluginDelegateStub::OnDidFinishLoadWithReason(
+ const GURL& url, int reason, int notify_id) {
+ delegate_->DidFinishLoadWithReason(url, reason, notify_id);
+}
+
+void WebPluginDelegateStub::OnSetFocus(bool focused) {
+ delegate_->SetFocus(focused);
+}
+
+void WebPluginDelegateStub::OnHandleInputEvent(
+ const WebKit::WebInputEvent *event,
+ bool* handled,
+ WebCursor* cursor) {
+ WebCursorInfo cursor_info;
+ *handled = delegate_->HandleInputEvent(*event, &cursor_info);
+ cursor->InitFromCursorInfo(cursor_info);
+}
+
+void WebPluginDelegateStub::OnPaint(const gfx::Rect& damaged_rect) {
+ webplugin_->Paint(damaged_rect);
+}
+
+void WebPluginDelegateStub::OnDidPaint() {
+ webplugin_->DidPaint();
+}
+
+void WebPluginDelegateStub::OnPrint(base::SharedMemoryHandle* shared_memory,
+ uint32* size) {
+#if defined(OS_WIN)
+ scoped_ptr<printing::NativeMetafile> metafile(
+ printing::NativeMetafileFactory::CreateMetafile());
+ if (!metafile->CreateDc(NULL, NULL)) {
+ NOTREACHED();
+ return;
+ }
+ HDC hdc = metafile->context();
+ skia::PlatformDevice::InitializeDC(hdc);
+ delegate_->Print(hdc);
+ if (!metafile->Close()) {
+ NOTREACHED();
+ return;
+ }
+
+ *size = metafile->GetDataSize();
+ DCHECK(*size);
+ base::SharedMemory shared_buf;
+ CreateSharedBuffer(*size, &shared_buf, shared_memory);
+
+ // Retrieve a copy of the data.
+ bool success = metafile->GetData(shared_buf.memory(), *size);
+ DCHECK(success);
+#else
+ // TODO(port): plugin printing.
+ NOTIMPLEMENTED();
+#endif
+}
+
+void WebPluginDelegateStub::OnUpdateGeometry(
+ const PluginMsg_UpdateGeometry_Param& param) {
+ webplugin_->UpdateGeometry(
+ param.window_rect, param.clip_rect,
+ param.windowless_buffer, param.background_buffer,
+ param.transparent
+#if defined(OS_MACOSX)
+ ,
+ param.ack_key
+#endif
+ );
+}
+
+void WebPluginDelegateStub::OnGetPluginScriptableObject(int* route_id) {
+ NPObject* object = delegate_->GetPluginScriptableObject();
+ if (!object) {
+ *route_id = MSG_ROUTING_NONE;
+ return;
+ }
+
+ *route_id = channel_->GenerateRouteID();
+ // The stub will delete itself when the proxy tells it that it's released, or
+ // otherwise when the channel is closed.
+ new NPObjectStub(
+ object, channel_.get(), *route_id, webplugin_->containing_window(),
+ page_url_);
+
+ // Release ref added by GetPluginScriptableObject (our stub holds its own).
+ WebBindings::releaseObject(object);
+}
+
+void WebPluginDelegateStub::OnSendJavaScriptStream(const GURL& url,
+ const std::string& result,
+ bool success,
+ int notify_id) {
+ delegate_->SendJavaScriptStream(url, result, success, notify_id);
+}
+
+void WebPluginDelegateStub::OnSetContentAreaFocus(bool has_focus) {
+ if (delegate_)
+ delegate_->SetContentAreaHasFocus(has_focus);
+}
+
+#if defined(OS_MACOSX)
+void WebPluginDelegateStub::OnSetWindowFocus(bool has_focus) {
+ if (delegate_)
+ delegate_->SetWindowHasFocus(has_focus);
+}
+
+void WebPluginDelegateStub::OnContainerHidden() {
+ if (delegate_)
+ delegate_->SetContainerVisibility(false);
+}
+
+void WebPluginDelegateStub::OnContainerShown(gfx::Rect window_frame,
+ gfx::Rect view_frame,
+ bool has_focus) {
+ if (delegate_) {
+ delegate_->WindowFrameChanged(window_frame, view_frame);
+ delegate_->SetContainerVisibility(true);
+ delegate_->SetWindowHasFocus(has_focus);
+ }
+}
+
+void WebPluginDelegateStub::OnWindowFrameChanged(const gfx::Rect& window_frame,
+ const gfx::Rect& view_frame) {
+ if (delegate_)
+ delegate_->WindowFrameChanged(window_frame, view_frame);
+}
+
+void WebPluginDelegateStub::OnImeCompositionCompleted(const string16& text) {
+ if (delegate_)
+ delegate_->ImeCompositionCompleted(text);
+}
+#endif // OS_MACOSX
+
+void WebPluginDelegateStub::OnDidReceiveManualResponse(
+ const GURL& url,
+ const PluginMsg_DidReceiveResponseParams& params) {
+ delegate_->DidReceiveManualResponse(url, params.mime_type, params.headers,
+ params.expected_length,
+ params.last_modified);
+}
+
+void WebPluginDelegateStub::OnDidReceiveManualData(
+ const std::vector<char>& buffer) {
+ delegate_->DidReceiveManualData(&buffer.front(),
+ static_cast<int>(buffer.size()));
+}
+
+void WebPluginDelegateStub::OnDidFinishManualLoading() {
+ delegate_->DidFinishManualLoading();
+}
+
+void WebPluginDelegateStub::OnDidManualLoadFail() {
+ delegate_->DidManualLoadFail();
+}
+
+void WebPluginDelegateStub::OnInstallMissingPlugin() {
+ delegate_->InstallMissingPlugin();
+}
+
+void WebPluginDelegateStub::CreateSharedBuffer(
+ uint32 size,
+ base::SharedMemory* shared_buf,
+ base::SharedMemoryHandle* remote_handle) {
+ if (!shared_buf->CreateAndMapAnonymous(size)) {
+ NOTREACHED();
+ shared_buf->Close();
+ return;
+ }
+
+#if defined(OS_WIN)
+ BOOL result = DuplicateHandle(GetCurrentProcess(),
+ shared_buf->handle(),
+ channel_->renderer_handle(),
+ remote_handle, 0, FALSE,
+ DUPLICATE_SAME_ACCESS);
+ DCHECK_NE(result, 0);
+
+ // If the calling function's shared_buf is on the stack, its destructor will
+ // close the shared memory buffer handle. This is fine since we already
+ // duplicated the handle to the renderer process so it will stay "alive".
+#else
+ // TODO(port): this should use TransportDIB.
+ NOTIMPLEMENTED();
+#endif
+}
+
+void WebPluginDelegateStub::OnHandleURLRequestReply(
+ unsigned long resource_id, const GURL& url, int notify_id) {
+ WebPluginResourceClient* resource_client =
+ delegate_->CreateResourceClient(resource_id, url, notify_id);
+ webplugin_->OnResourceCreated(resource_id, resource_client);
+}
+
+void WebPluginDelegateStub::OnHTTPRangeRequestReply(
+ unsigned long resource_id, int range_request_id) {
+ WebPluginResourceClient* resource_client =
+ delegate_->CreateSeekableResourceClient(resource_id, range_request_id);
+ webplugin_->OnResourceCreated(resource_id, resource_client);
+}
+
+#if defined(OS_MACOSX)
+void WebPluginDelegateStub::OnSetFakeAcceleratedSurfaceWindowHandle(
+ gfx::PluginWindowHandle window) {
+ delegate_->set_windowed_handle(window);
+}
+#endif
+
diff --git a/content/plugin/webplugin_delegate_stub.h b/content/plugin/webplugin_delegate_stub.h
new file mode 100644
index 0000000..74fd5bd
--- /dev/null
+++ b/content/plugin/webplugin_delegate_stub.h
@@ -0,0 +1,135 @@
+// Copyright (c) 2010 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.
+
+#ifndef CONTENT_PLUGIN_WEBPLUGIN_DELEGATE_STUB_H_
+#define CONTENT_PLUGIN_WEBPLUGIN_DELEGATE_STUB_H_
+#pragma once
+
+#include <string>
+#include <vector>
+
+#include "base/ref_counted.h"
+#include "base/shared_memory.h"
+#include "base/task.h"
+#include "googleurl/src/gurl.h"
+#include "ipc/ipc_channel.h"
+#include "third_party/npapi/bindings/npapi.h"
+#include "ui/gfx/native_widget_types.h"
+#include "ui/gfx/rect.h"
+
+class PluginChannel;
+class WebPluginProxy;
+struct PluginMsg_Init_Params;
+struct PluginMsg_DidReceiveResponseParams;
+struct PluginMsg_UpdateGeometry_Param;
+class WebCursor;
+
+namespace WebKit {
+class WebInputEvent;
+}
+
+namespace webkit {
+namespace npapi {
+class WebPluginDelegateImpl;
+}
+}
+
+// Converts the IPC messages from WebPluginDelegateProxy into calls to the
+// actual WebPluginDelegateImpl object.
+class WebPluginDelegateStub : public IPC::Channel::Listener,
+ public IPC::Message::Sender,
+ public base::RefCounted<WebPluginDelegateStub> {
+ public:
+ WebPluginDelegateStub(const std::string& mime_type, int instance_id,
+ PluginChannel* channel);
+
+ // IPC::Channel::Listener implementation:
+ virtual bool OnMessageReceived(const IPC::Message& msg);
+
+ // IPC::Message::Sender implementation:
+ virtual bool Send(IPC::Message* msg);
+
+ int instance_id() { return instance_id_; }
+ WebPluginProxy* webplugin() { return webplugin_; }
+
+ private:
+ friend class base::RefCounted<WebPluginDelegateStub>;
+
+ ~WebPluginDelegateStub();
+
+ // Message handlers for the WebPluginDelegate calls that are proxied from the
+ // renderer over the IPC channel.
+ void OnInit(const PluginMsg_Init_Params& params, bool* result);
+ void OnWillSendRequest(int id, const GURL& url, int http_status_code);
+ void OnDidReceiveResponse(const PluginMsg_DidReceiveResponseParams& params);
+ void OnDidReceiveData(int id, const std::vector<char>& buffer,
+ int data_offset);
+ void OnDidFinishLoading(int id);
+ void OnDidFail(int id);
+ void OnDidFinishLoadWithReason(const GURL& url, int reason, int notify_id);
+ void OnSetFocus(bool focused);
+ void OnHandleInputEvent(const WebKit::WebInputEvent* event,
+ bool* handled, WebCursor* cursor);
+ void OnPaint(const gfx::Rect& damaged_rect);
+ void OnDidPaint();
+ void OnPrint(base::SharedMemoryHandle* shared_memory, uint32* size);
+ void OnUpdateGeometry(const PluginMsg_UpdateGeometry_Param& param);
+ void OnGetPluginScriptableObject(int* route_id);
+ void OnSendJavaScriptStream(const GURL& url,
+ const std::string& result,
+ bool success,
+ int notify_id);
+
+ void OnSetContentAreaFocus(bool has_focus);
+#if defined(OS_MACOSX)
+ void OnSetWindowFocus(bool has_focus);
+ void OnContainerHidden();
+ void OnContainerShown(gfx::Rect window_frame, gfx::Rect view_frame,
+ bool has_focus);
+ void OnWindowFrameChanged(const gfx::Rect& window_frame,
+ const gfx::Rect& view_frame);
+ void OnImeCompositionCompleted(const string16& text);
+#endif
+
+ void OnDidReceiveManualResponse(
+ const GURL& url,
+ const PluginMsg_DidReceiveResponseParams& params);
+ void OnDidReceiveManualData(const std::vector<char>& buffer);
+ void OnDidFinishManualLoading();
+ void OnDidManualLoadFail();
+ void OnInstallMissingPlugin();
+ void OnHandleURLRequestReply(unsigned long resource_id,
+ const GURL& url,
+ int notify_id);
+ void OnHTTPRangeRequestReply(unsigned long resource_id, int range_request_id);
+
+ void CreateSharedBuffer(uint32 size,
+ base::SharedMemory* shared_buf,
+ base::SharedMemoryHandle* remote_handle);
+
+ std::string mime_type_;
+ int instance_id_;
+
+ scoped_refptr<PluginChannel> channel_;
+
+ webkit::npapi::WebPluginDelegateImpl* delegate_;
+ WebPluginProxy* webplugin_;
+ bool in_destructor_;
+
+ // The url of the main frame hosting the plugin.
+ GURL page_url_;
+
+#if defined(ENABLE_GPU)
+#if defined(OS_MACOSX)
+ // If this is a GPU-accelerated plug-in, we need to be able to receive a fake
+ // window handle which is used for subsequent communication back to the
+ // browser.
+ void OnSetFakeAcceleratedSurfaceWindowHandle(gfx::PluginWindowHandle window);
+#endif
+#endif
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(WebPluginDelegateStub);
+};
+
+#endif // CONTENT_PLUGIN_WEBPLUGIN_DELEGATE_STUB_H_
diff --git a/content/plugin/webplugin_proxy.cc b/content/plugin/webplugin_proxy.cc
new file mode 100644
index 0000000..57e61b2
--- /dev/null
+++ b/content/plugin/webplugin_proxy.cc
@@ -0,0 +1,656 @@
+// Copyright (c) 2011 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/plugin/webplugin_proxy.h"
+
+#include "build/build_config.h"
+
+#include "base/lazy_instance.h"
+#include "base/scoped_handle.h"
+#include "base/shared_memory.h"
+#include "build/build_config.h"
+#include "content/common/content_client.h"
+#include "content/common/plugin_messages.h"
+#include "content/plugin/npobject_proxy.h"
+#include "content/plugin/npobject_util.h"
+#include "content/plugin/plugin_channel.h"
+#include "content/plugin/plugin_thread.h"
+#include "skia/ext/platform_device.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebBindings.h"
+#include "ui/gfx/blit.h"
+#include "ui/gfx/canvas.h"
+#include "webkit/plugins/npapi/webplugin_delegate_impl.h"
+
+#if defined(OS_MACOSX)
+#include "base/mac/mac_util.h"
+#include "base/mac/scoped_cftyperef.h"
+#include "content/plugin/webplugin_accelerated_surface_proxy_mac.h"
+#endif
+
+#if defined(OS_WIN)
+#include "content/common/section_util_win.h"
+#include "ui/gfx/gdi_util.h"
+#endif
+
+#if defined(USE_X11)
+#include "ui/base/x/x11_util_internal.h"
+#endif
+
+using WebKit::WebBindings;
+
+using webkit::npapi::WebPluginResourceClient;
+#if defined(OS_MACOSX)
+using webkit::npapi::WebPluginAcceleratedSurface;
+#endif
+
+WebPluginProxy::WebPluginProxy(
+ PluginChannel* channel,
+ int route_id,
+ const GURL& page_url,
+ gfx::NativeViewId containing_window,
+ int host_render_view_routing_id)
+ : channel_(channel),
+ route_id_(route_id),
+ window_npobject_(NULL),
+ plugin_element_(NULL),
+ delegate_(NULL),
+ waiting_for_paint_(false),
+ containing_window_(containing_window),
+ page_url_(page_url),
+ transparent_(false),
+ host_render_view_routing_id_(host_render_view_routing_id),
+ ALLOW_THIS_IN_INITIALIZER_LIST(runnable_method_factory_(this)) {
+#if defined(USE_X11)
+ windowless_shm_pixmap_ = None;
+ use_shm_pixmap_ = false;
+
+ // If the X server supports SHM pixmaps
+ // and the color depth and masks match,
+ // then consider using SHM pixmaps for windowless plugin painting.
+ Display* display = ui::GetXDisplay();
+ if (ui::QuerySharedMemorySupport(display) == ui::SHARED_MEMORY_PIXMAP &&
+ ui::BitsPerPixelForPixmapDepth(
+ display, DefaultDepth(display, 0)) == 32) {
+ Visual* vis = DefaultVisual(display, 0);
+
+ if (vis->red_mask == 0xff0000 &&
+ vis->green_mask == 0xff00 &&
+ vis->blue_mask == 0xff)
+ use_shm_pixmap_ = true;
+ }
+#endif
+}
+
+WebPluginProxy::~WebPluginProxy() {
+#if defined(USE_X11)
+ if (windowless_shm_pixmap_ != None)
+ XFreePixmap(ui::GetXDisplay(), windowless_shm_pixmap_);
+#endif
+
+#if defined(OS_MACOSX)
+ // Destroy the surface early, since it may send messages during cleanup.
+ if (accelerated_surface_.get())
+ accelerated_surface_.reset();
+#endif
+}
+
+bool WebPluginProxy::Send(IPC::Message* msg) {
+ return channel_->Send(msg);
+}
+
+void WebPluginProxy::SetWindow(gfx::PluginWindowHandle window) {
+ Send(new PluginHostMsg_SetWindow(route_id_, window));
+}
+
+void WebPluginProxy::SetAcceptsInputEvents(bool accepts) {
+ NOTREACHED();
+}
+
+void WebPluginProxy::WillDestroyWindow(gfx::PluginWindowHandle window) {
+#if defined(OS_WIN)
+ PluginThread::current()->Send(
+ new PluginProcessHostMsg_PluginWindowDestroyed(
+ window, ::GetParent(window)));
+#elif defined(USE_X11)
+ // Nothing to do.
+#else
+ NOTIMPLEMENTED();
+#endif
+}
+
+#if defined(OS_WIN)
+void WebPluginProxy::SetWindowlessPumpEvent(HANDLE pump_messages_event) {
+ HANDLE pump_messages_event_for_renderer = NULL;
+ DuplicateHandle(GetCurrentProcess(), pump_messages_event,
+ channel_->renderer_handle(),
+ &pump_messages_event_for_renderer,
+ 0, FALSE, DUPLICATE_SAME_ACCESS);
+ DCHECK(pump_messages_event_for_renderer != NULL);
+ Send(new PluginHostMsg_SetWindowlessPumpEvent(
+ route_id_, pump_messages_event_for_renderer));
+}
+#endif
+
+void WebPluginProxy::CancelResource(unsigned long id) {
+ Send(new PluginHostMsg_CancelResource(route_id_, id));
+ resource_clients_.erase(id);
+}
+
+void WebPluginProxy::Invalidate() {
+ gfx::Rect rect(0, 0,
+ delegate_->GetRect().width(),
+ delegate_->GetRect().height());
+ InvalidateRect(rect);
+}
+
+void WebPluginProxy::InvalidateRect(const gfx::Rect& rect) {
+#if defined(OS_MACOSX)
+ // If this is a Core Animation plugin, all we need to do is inform the
+ // delegate.
+ if (!windowless_context_.get()) {
+ delegate_->PluginDidInvalidate();
+ return;
+ }
+
+ // Some plugins will send invalidates larger than their own rect when
+ // offscreen, so constrain invalidates to the plugin rect.
+ gfx::Rect plugin_rect = delegate_->GetRect();
+ plugin_rect.set_origin(gfx::Point(0, 0));
+ const gfx::Rect invalidate_rect(rect.Intersect(plugin_rect));
+#else
+ const gfx::Rect invalidate_rect(rect);
+#endif
+ damaged_rect_ = damaged_rect_.Union(invalidate_rect);
+ // Ignore NPN_InvalidateRect calls with empty rects. Also don't send an
+ // invalidate if it's outside the clipping region, since if we did it won't
+ // lead to a paint and we'll be stuck waiting forever for a DidPaint response.
+ //
+ // TODO(piman): There is a race condition here, because this test assumes
+ // that when the paint actually occurs, the clip rect will not have changed.
+ // This is not true because scrolling (or window resize) could occur and be
+ // handled by the renderer before it receives the InvalidateRect message,
+ // changing the clip rect and then not painting.
+ if (damaged_rect_.IsEmpty() ||
+ !delegate_->GetClipRect().Intersects(damaged_rect_))
+ return;
+
+ // Only send a single InvalidateRect message at a time. From DidPaint we
+ // will dispatch an additional InvalidateRect message if necessary.
+ if (!waiting_for_paint_) {
+ waiting_for_paint_ = true;
+ // Invalidates caused by calls to NPN_InvalidateRect/NPN_InvalidateRgn
+ // need to be painted asynchronously as per the NPAPI spec.
+ MessageLoop::current()->PostTask(FROM_HERE,
+ runnable_method_factory_.NewRunnableMethod(
+ &WebPluginProxy::OnPaint, damaged_rect_));
+ damaged_rect_ = gfx::Rect();
+ }
+}
+
+NPObject* WebPluginProxy::GetWindowScriptNPObject() {
+ if (window_npobject_)
+ return WebBindings::retainObject(window_npobject_);
+
+ int npobject_route_id = channel_->GenerateRouteID();
+ bool success = false;
+ Send(new PluginHostMsg_GetWindowScriptNPObject(
+ route_id_, npobject_route_id, &success));
+ if (!success)
+ return NULL;
+
+ window_npobject_ = NPObjectProxy::Create(
+ channel_, npobject_route_id, containing_window_, page_url_);
+
+ return window_npobject_;
+}
+
+NPObject* WebPluginProxy::GetPluginElement() {
+ if (plugin_element_)
+ return WebBindings::retainObject(plugin_element_);
+
+ int npobject_route_id = channel_->GenerateRouteID();
+ bool success = false;
+ Send(new PluginHostMsg_GetPluginElement(route_id_, npobject_route_id,
+ &success));
+ if (!success)
+ return NULL;
+
+ plugin_element_ = NPObjectProxy::Create(
+ channel_, npobject_route_id, containing_window_, page_url_);
+
+ return plugin_element_;
+}
+
+void WebPluginProxy::SetCookie(const GURL& url,
+ const GURL& first_party_for_cookies,
+ const std::string& cookie) {
+ Send(new PluginHostMsg_SetCookie(route_id_, url,
+ first_party_for_cookies, cookie));
+}
+
+std::string WebPluginProxy::GetCookies(const GURL& url,
+ const GURL& first_party_for_cookies) {
+ std::string cookies;
+ Send(new PluginHostMsg_GetCookies(route_id_, url,
+ first_party_for_cookies, &cookies));
+
+ return cookies;
+}
+
+void WebPluginProxy::OnMissingPluginStatus(int status) {
+ Send(new PluginHostMsg_MissingPluginStatus(route_id_, status));
+}
+
+WebPluginResourceClient* WebPluginProxy::GetResourceClient(int id) {
+ ResourceClientMap::iterator iterator = resource_clients_.find(id);
+ // The IPC messages which deal with streams are now asynchronous. It is
+ // now possible to receive stream messages from the renderer for streams
+ // which may have been cancelled by the plugin.
+ if (iterator == resource_clients_.end()) {
+ return NULL;
+ }
+
+ return iterator->second;
+}
+
+int WebPluginProxy::GetRendererId() {
+ if (channel_.get())
+ return channel_->renderer_id();
+ return -1;
+}
+
+void WebPluginProxy::DidPaint() {
+ // If we have an accumulated damaged rect, then check to see if we need to
+ // send out another InvalidateRect message.
+ waiting_for_paint_ = false;
+ if (!damaged_rect_.IsEmpty())
+ InvalidateRect(damaged_rect_);
+}
+
+void WebPluginProxy::OnResourceCreated(int resource_id,
+ WebPluginResourceClient* client) {
+ DCHECK(resource_clients_.find(resource_id) == resource_clients_.end());
+ resource_clients_[resource_id] = client;
+}
+
+void WebPluginProxy::HandleURLRequest(const char* url,
+ const char* method,
+ const char* target,
+ const char* buf,
+ unsigned int len,
+ int notify_id,
+ bool popups_allowed,
+ bool notify_redirects) {
+ if (!target && (0 == base::strcasecmp(method, "GET"))) {
+ // Please refer to https://bugzilla.mozilla.org/show_bug.cgi?id=366082
+ // for more details on this.
+ if (delegate_->GetQuirks() &
+ webkit::npapi::WebPluginDelegateImpl::
+ PLUGIN_QUIRK_BLOCK_NONSTANDARD_GETURL_REQUESTS) {
+ GURL request_url(url);
+ if (!request_url.SchemeIs("http") &&
+ !request_url.SchemeIs("https") &&
+ !request_url.SchemeIs("ftp")) {
+ return;
+ }
+ }
+ }
+
+ PluginHostMsg_URLRequest_Params params;
+ params.url = url;
+ params.method = method;
+ if (target)
+ params.target = std::string(target);
+
+ if (len) {
+ params.buffer.resize(len);
+ memcpy(&params.buffer.front(), buf, len);
+ }
+
+ params.notify_id = notify_id;
+ params.popups_allowed = popups_allowed;
+ params.notify_redirects = notify_redirects;
+
+ Send(new PluginHostMsg_URLRequest(route_id_, params));
+}
+
+void WebPluginProxy::Paint(const gfx::Rect& rect) {
+#if defined(OS_MACOSX)
+ if (!windowless_context_.get())
+ return;
+#else
+ if (!windowless_canvas_.get())
+ return;
+#endif
+
+ // Clear the damaged area so that if the plugin doesn't paint there we won't
+ // end up with the old values.
+ gfx::Rect offset_rect = rect;
+ offset_rect.Offset(delegate_->GetRect().origin());
+#if defined(OS_MACOSX)
+ CGContextSaveGState(windowless_context_);
+ // It is possible for windowless_context_ to change during plugin painting
+ // (since the plugin can make a synchronous call during paint event handling),
+ // in which case we don't want to try to restore it later. Not an owning ref
+ // since owning the ref without owning the shared backing memory doesn't make
+ // sense, so this should only be used for pointer comparisons.
+ CGContextRef saved_context_weak = windowless_context_.get();
+
+ if (background_context_.get()) {
+ base::mac::ScopedCFTypeRef<CGImageRef> image(
+ CGBitmapContextCreateImage(background_context_));
+ CGRect source_rect = rect.ToCGRect();
+ // Flip the rect we use to pull from the canvas, since it's upside-down.
+ source_rect.origin.y = CGImageGetHeight(image) - rect.y() - rect.height();
+ base::mac::ScopedCFTypeRef<CGImageRef> sub_image(
+ CGImageCreateWithImageInRect(image, source_rect));
+ CGContextDrawImage(windowless_context_, rect.ToCGRect(), sub_image);
+ } else if (transparent_) {
+ CGContextClearRect(windowless_context_, rect.ToCGRect());
+ }
+ CGContextClipToRect(windowless_context_, rect.ToCGRect());
+ delegate_->Paint(windowless_context_, rect);
+ if (windowless_context_.get() == saved_context_weak)
+ CGContextRestoreGState(windowless_context_);
+#else
+ windowless_canvas_->save();
+
+ // The given clip rect is relative to the plugin coordinate system.
+ SkRect sk_rect = { SkIntToScalar(rect.x()),
+ SkIntToScalar(rect.y()),
+ SkIntToScalar(rect.right()),
+ SkIntToScalar(rect.bottom()) };
+ windowless_canvas_->clipRect(sk_rect);
+
+ // Setup the background.
+ if (background_canvas_.get()) {
+ // When a background canvas is given, we're in transparent mode. This means
+ // the plugin wants to have the image of the page in the canvas it's drawing
+ // into (which is windowless_canvas_) so it can do blending. So we copy the
+ // background bitmap into the windowless_canvas_.
+ const SkBitmap& background_bitmap =
+ background_canvas_->getTopPlatformDevice().accessBitmap(false);
+ windowless_canvas_->drawBitmap(background_bitmap, 0, 0);
+ } else {
+ // In non-transparent mode, the plugin doesn't care what's underneath, so we
+ // can just give it black.
+ SkPaint black_fill_paint;
+ black_fill_paint.setARGB(0xFF, 0x00, 0x00, 0x00);
+ windowless_canvas_->drawPaint(black_fill_paint);
+ }
+
+ // Bring the windowless_canvas_ into the window coordinate system, which is
+ // how the plugin expects to draw (since the windowless API was originally
+ // designed just for scribbling over the web page).
+ windowless_canvas_->translate(SkIntToScalar(-delegate_->GetRect().x()),
+ SkIntToScalar(-delegate_->GetRect().y()));
+
+ // Before we send the invalidate, paint so that renderer uses the updated
+ // bitmap.
+ delegate_->Paint(windowless_canvas_.get(), offset_rect);
+
+ windowless_canvas_->restore();
+#endif
+}
+
+void WebPluginProxy::UpdateGeometry(
+ const gfx::Rect& window_rect,
+ const gfx::Rect& clip_rect,
+ const TransportDIB::Handle& windowless_buffer,
+ const TransportDIB::Handle& background_buffer,
+ bool transparent
+#if defined(OS_MACOSX)
+ ,
+ int ack_key
+#endif
+ ) {
+ gfx::Rect old = delegate_->GetRect();
+ gfx::Rect old_clip_rect = delegate_->GetClipRect();
+ transparent_ = transparent;
+
+ // Update the buffers before doing anything that could call into plugin code,
+ // so that we don't process buffer changes out of order if plugins make
+ // synchronous calls that lead to nested UpdateGeometry calls.
+ if (TransportDIB::is_valid(windowless_buffer)) {
+ // The plugin's rect changed, so now we have a new buffer to draw into.
+ SetWindowlessBuffer(windowless_buffer, background_buffer, window_rect);
+ }
+
+#if defined(OS_MACOSX)
+ delegate_->UpdateGeometryAndContext(window_rect, clip_rect,
+ windowless_context_);
+#else
+ delegate_->UpdateGeometry(window_rect, clip_rect);
+#endif
+
+ // Send over any pending invalidates which occured when the plugin was
+ // off screen.
+ if (delegate_->IsWindowless() && !clip_rect.IsEmpty() &&
+ !damaged_rect_.IsEmpty()) {
+ InvalidateRect(damaged_rect_);
+ }
+
+#if defined(OS_MACOSX)
+ // The renderer is expecting an ACK message if ack_key is not -1.
+ if (ack_key != -1) {
+ Send(new PluginHostMsg_UpdateGeometry_ACK(route_id_, ack_key));
+ }
+#endif
+}
+
+#if defined(OS_WIN)
+void WebPluginProxy::SetWindowlessBuffer(
+ const TransportDIB::Handle& windowless_buffer,
+ const TransportDIB::Handle& background_buffer,
+ const gfx::Rect& window_rect) {
+ // Create a canvas that will reference the shared bits. We have to handle
+ // errors here since we're mapping a large amount of memory that may not fit
+ // in our address space, or go wrong in some other way.
+ windowless_canvas_.reset(new skia::PlatformCanvas);
+ if (!windowless_canvas_->initialize(
+ window_rect.width(),
+ window_rect.height(),
+ true,
+ chrome::GetSectionFromProcess(windowless_buffer,
+ channel_->renderer_handle(), false))) {
+ windowless_canvas_.reset();
+ background_canvas_.reset();
+ return;
+ }
+
+ if (background_buffer) {
+ background_canvas_.reset(new skia::PlatformCanvas);
+ if (!background_canvas_->initialize(
+ window_rect.width(),
+ window_rect.height(),
+ true,
+ chrome::GetSectionFromProcess(background_buffer,
+ channel_->renderer_handle(), false))) {
+ windowless_canvas_.reset();
+ background_canvas_.reset();
+ return;
+ }
+ }
+}
+
+#elif defined(OS_MACOSX)
+
+void WebPluginProxy::SetWindowlessBuffer(
+ const TransportDIB::Handle& windowless_buffer,
+ const TransportDIB::Handle& background_buffer,
+ const gfx::Rect& window_rect) {
+ // Convert the shared memory handle to a handle that works in our process,
+ // and then use that to create a CGContextRef.
+ windowless_dib_.reset(TransportDIB::Map(windowless_buffer));
+ background_dib_.reset(TransportDIB::Map(background_buffer));
+ windowless_context_.reset(CGBitmapContextCreate(
+ windowless_dib_->memory(),
+ window_rect.width(),
+ window_rect.height(),
+ 8, 4 * window_rect.width(),
+ base::mac::GetSystemColorSpace(),
+ kCGImageAlphaPremultipliedFirst |
+ kCGBitmapByteOrder32Host));
+ CGContextTranslateCTM(windowless_context_, 0, window_rect.height());
+ CGContextScaleCTM(windowless_context_, 1, -1);
+ if (background_dib_.get()) {
+ background_context_.reset(CGBitmapContextCreate(
+ background_dib_->memory(),
+ window_rect.width(),
+ window_rect.height(),
+ 8, 4 * window_rect.width(),
+ base::mac::GetSystemColorSpace(),
+ kCGImageAlphaPremultipliedFirst |
+ kCGBitmapByteOrder32Host));
+ CGContextTranslateCTM(background_context_, 0, window_rect.height());
+ CGContextScaleCTM(background_context_, 1, -1);
+ }
+}
+
+#elif defined(USE_X11)
+
+void WebPluginProxy::SetWindowlessBuffer(
+ const TransportDIB::Handle& windowless_buffer,
+ const TransportDIB::Handle& background_buffer,
+ const gfx::Rect& window_rect) {
+ int width = window_rect.width();
+ int height = window_rect.height();
+ windowless_dib_.reset(TransportDIB::Map(windowless_buffer));
+ if (windowless_dib_.get()) {
+ windowless_canvas_.reset(windowless_dib_->GetPlatformCanvas(width, height));
+ } else {
+ // This can happen if the renderer has already destroyed the TransportDIB
+ // by the time we receive the handle, e.g. in case of multiple resizes.
+ windowless_canvas_.reset();
+ }
+ background_dib_.reset(TransportDIB::Map(background_buffer));
+ if (background_dib_.get()) {
+ background_canvas_.reset(background_dib_->GetPlatformCanvas(width, height));
+ } else {
+ background_canvas_.reset();
+ }
+
+ // If SHM pixmaps support is available, create a SHM pixmap and
+ // pass it to the delegate for windowless plugin painting.
+ if (delegate_->IsWindowless() && use_shm_pixmap_ && windowless_dib_.get()) {
+ Display* display = ui::GetXDisplay();
+ XID root_window = ui::GetX11RootWindow();
+ XShmSegmentInfo shminfo = {0};
+
+ if (windowless_shm_pixmap_ != None)
+ XFreePixmap(display, windowless_shm_pixmap_);
+
+ shminfo.shmseg = windowless_dib_->MapToX(display);
+ // Create a shared memory pixmap based on the image buffer.
+ windowless_shm_pixmap_ = XShmCreatePixmap(display, root_window,
+ NULL, &shminfo,
+ width, height,
+ DefaultDepth(display, 0));
+
+ delegate_->SetWindowlessShmPixmap(windowless_shm_pixmap_);
+ }
+}
+
+#endif
+
+void WebPluginProxy::CancelDocumentLoad() {
+ Send(new PluginHostMsg_CancelDocumentLoad(route_id_));
+}
+
+void WebPluginProxy::InitiateHTTPRangeRequest(
+ const char* url, const char* range_info, int range_request_id) {
+ Send(new PluginHostMsg_InitiateHTTPRangeRequest(
+ route_id_, url, range_info, range_request_id));
+}
+
+void WebPluginProxy::SetDeferResourceLoading(unsigned long resource_id,
+ bool defer) {
+ Send(new PluginHostMsg_DeferResourceLoading(route_id_, resource_id, defer));
+}
+
+#if defined(OS_MACOSX)
+void WebPluginProxy::FocusChanged(bool focused) {
+ IPC::Message* msg = new PluginHostMsg_FocusChanged(route_id_, focused);
+ Send(msg);
+}
+
+void WebPluginProxy::StartIme() {
+ IPC::Message* msg = new PluginHostMsg_StartIme(route_id_);
+ // This message can be sent during event-handling, and needs to be delivered
+ // within that context.
+ msg->set_unblock(true);
+ Send(msg);
+}
+
+void WebPluginProxy::BindFakePluginWindowHandle(bool opaque) {
+ Send(new PluginHostMsg_BindFakePluginWindowHandle(route_id_, opaque));
+}
+
+WebPluginAcceleratedSurface* WebPluginProxy::GetAcceleratedSurface() {
+ if (!accelerated_surface_.get())
+ accelerated_surface_.reset(new WebPluginAcceleratedSurfaceProxy(this));
+ return accelerated_surface_.get();
+}
+
+void WebPluginProxy::AcceleratedFrameBuffersDidSwap(
+ gfx::PluginWindowHandle window, uint64 surface_id) {
+ Send(new PluginHostMsg_AcceleratedSurfaceBuffersSwapped(
+ route_id_, window, surface_id));
+}
+
+void WebPluginProxy::SetAcceleratedSurface(
+ gfx::PluginWindowHandle window,
+ const gfx::Size& size,
+ uint64 accelerated_surface_identifier) {
+ Send(new PluginHostMsg_AcceleratedSurfaceSetIOSurface(
+ route_id_, window, size.width(), size.height(),
+ accelerated_surface_identifier));
+}
+
+void WebPluginProxy::SetAcceleratedDIB(
+ gfx::PluginWindowHandle window,
+ const gfx::Size& size,
+ const TransportDIB::Handle& dib_handle) {
+ Send(new PluginHostMsg_AcceleratedSurfaceSetTransportDIB(
+ route_id_, window, size.width(), size.height(), dib_handle));
+}
+
+void WebPluginProxy::AllocSurfaceDIB(const size_t size,
+ TransportDIB::Handle* dib_handle) {
+ Send(new PluginHostMsg_AllocTransportDIB(route_id_, size, dib_handle));
+}
+
+void WebPluginProxy::FreeSurfaceDIB(TransportDIB::Id dib_id) {
+ Send(new PluginHostMsg_FreeTransportDIB(route_id_, dib_id));
+}
+#endif
+
+void WebPluginProxy::OnPaint(const gfx::Rect& damaged_rect) {
+ content::GetContentClient()->SetActiveURL(page_url_);
+
+ Paint(damaged_rect);
+ Send(new PluginHostMsg_InvalidateRect(route_id_, damaged_rect));
+}
+
+bool WebPluginProxy::IsOffTheRecord() {
+ return channel_->incognito();
+}
+
+void WebPluginProxy::ResourceClientDeleted(
+ WebPluginResourceClient* resource_client) {
+ ResourceClientMap::iterator index = resource_clients_.begin();
+ while (index != resource_clients_.end()) {
+ WebPluginResourceClient* client = (*index).second;
+
+ if (client == resource_client) {
+ resource_clients_.erase(index++);
+ } else {
+ index++;
+ }
+ }
+}
+
+void WebPluginProxy::URLRedirectResponse(bool allow, int resource_id) {
+ Send(new PluginHostMsg_URLRedirectResponse(route_id_, allow, resource_id));
+}
diff --git a/content/plugin/webplugin_proxy.h b/content/plugin/webplugin_proxy.h
new file mode 100644
index 0000000..8dbfa9e
--- /dev/null
+++ b/content/plugin/webplugin_proxy.h
@@ -0,0 +1,225 @@
+// Copyright (c) 2010 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.
+
+#ifndef CONTENT_PLUGIN_WEBPLUGIN_PROXY_H_
+#define CONTENT_PLUGIN_WEBPLUGIN_PROXY_H_
+#pragma once
+
+#include <string>
+
+#if defined(USE_X11)
+#include "ui/base/x/x11_util.h"
+#endif
+#include "app/surface/transport_dib.h"
+#include "base/hash_tables.h"
+#include "base/ref_counted.h"
+#if defined(OS_MACOSX)
+#include "base/mac/scoped_cftyperef.h"
+#endif
+#include "base/scoped_handle.h"
+#include "base/scoped_ptr.h"
+#include "base/shared_memory.h"
+#include "base/timer.h"
+#include "googleurl/src/gurl.h"
+#include "ipc/ipc_message.h"
+#include "webkit/plugins/npapi/webplugin.h"
+
+class PluginChannel;
+
+namespace webkit {
+namespace npapi {
+class WebPluginDelegateImpl;
+}
+}
+
+#if defined(OS_MACOSX)
+class WebPluginAcceleratedSurfaceProxy;
+#endif
+
+// This is an implementation of WebPlugin that proxies all calls to the
+// renderer.
+class WebPluginProxy : public webkit::npapi::WebPlugin {
+ public:
+ // Creates a new proxy for WebPlugin, using the given sender to send the
+ // marshalled WebPlugin calls.
+ WebPluginProxy(PluginChannel* channel,
+ int route_id,
+ const GURL& page_url,
+ gfx::NativeViewId containing_window,
+ int host_render_view_routing_id);
+ ~WebPluginProxy();
+
+ void set_delegate(webkit::npapi::WebPluginDelegateImpl* d) { delegate_ = d; }
+
+ // WebPlugin overrides
+ virtual void SetWindow(gfx::PluginWindowHandle window);
+
+ // Whether input events should be sent to the delegate.
+ virtual void SetAcceptsInputEvents(bool accepts);
+
+ virtual void WillDestroyWindow(gfx::PluginWindowHandle window);
+#if defined(OS_WIN)
+ void SetWindowlessPumpEvent(HANDLE pump_messages_event);
+#endif
+
+ virtual void CancelResource(unsigned long id);
+ virtual void Invalidate();
+ virtual void InvalidateRect(const gfx::Rect& rect);
+ virtual NPObject* GetWindowScriptNPObject();
+ virtual NPObject* GetPluginElement();
+ virtual void SetCookie(const GURL& url,
+ const GURL& first_party_for_cookies,
+ const std::string& cookie);
+ virtual std::string GetCookies(const GURL& url,
+ const GURL& first_party_for_cookies);
+
+ virtual void OnMissingPluginStatus(int status);
+ // class-specific methods
+
+ // Returns a WebPluginResourceClient object given its id, or NULL if no
+ // object with that id exists.
+ webkit::npapi::WebPluginResourceClient* GetResourceClient(int id);
+
+ // Returns the id of the renderer that contains this plugin.
+ int GetRendererId();
+
+ // Returns the id of the associated render view.
+ int host_render_view_routing_id() const {
+ return host_render_view_routing_id_;
+ }
+
+ // For windowless plugins, paints the given rectangle into the local buffer.
+ void Paint(const gfx::Rect& rect);
+
+ // Callback from the renderer to let us know that a paint occurred.
+ void DidPaint();
+
+ // Notification received on a plugin issued resource request creation.
+ void OnResourceCreated(int resource_id,
+ webkit::npapi::WebPluginResourceClient* client);
+
+ virtual void HandleURLRequest(const char* url,
+ const char* method,
+ const char* target,
+ const char* buf,
+ unsigned int len,
+ int notify_id,
+ bool popups_allowed,
+ bool notify_redirects);
+ void UpdateGeometry(const gfx::Rect& window_rect,
+ const gfx::Rect& clip_rect,
+ const TransportDIB::Handle& windowless_buffer,
+ const TransportDIB::Handle& background_buffer,
+ bool transparent
+#if defined(OS_MACOSX)
+ ,
+ int ack_key
+#endif
+ );
+ virtual void CancelDocumentLoad();
+ virtual void InitiateHTTPRangeRequest(
+ const char* url, const char* range_info, int range_request_id);
+ virtual void SetDeferResourceLoading(unsigned long resource_id, bool defer);
+ virtual bool IsOffTheRecord();
+ virtual void ResourceClientDeleted(
+ webkit::npapi::WebPluginResourceClient* resource_client);
+ gfx::NativeViewId containing_window() { return containing_window_; }
+
+#if defined(OS_MACOSX)
+ virtual void FocusChanged(bool focused);
+
+ virtual void StartIme();
+
+ virtual void BindFakePluginWindowHandle(bool opaque);
+
+ virtual webkit::npapi::WebPluginAcceleratedSurface* GetAcceleratedSurface();
+
+ // Tell the browser (via the renderer) to invalidate because the
+ // accelerated buffers have changed.
+ virtual void AcceleratedFrameBuffersDidSwap(
+ gfx::PluginWindowHandle window, uint64 surface_id);
+
+ // Tell the renderer and browser to associate the given plugin handle with
+ // |accelerated_surface_identifier|. The geometry is used to resize any
+ // native "window" (which on the Mac is a just a view).
+ // This method is used when IOSurface support is available.
+ virtual void SetAcceleratedSurface(gfx::PluginWindowHandle window,
+ const gfx::Size& size,
+ uint64 accelerated_surface_identifier);
+
+ // Tell the renderer and browser to associate the given plugin handle with
+ // |dib_handle|. The geometry is used to resize any native "window" (which
+ // on the Mac is just a view).
+ // This method is used when IOSurface support is not available.
+ virtual void SetAcceleratedDIB(
+ gfx::PluginWindowHandle window,
+ const gfx::Size& size,
+ const TransportDIB::Handle& dib_handle);
+
+ // Create/destroy TranportDIBs via messages to the browser process.
+ // These are only used when IOSurface support is not available.
+ virtual void AllocSurfaceDIB(const size_t size,
+ TransportDIB::Handle* dib_handle);
+ virtual void FreeSurfaceDIB(TransportDIB::Id dib_id);
+#endif
+
+ virtual void URLRedirectResponse(bool allow, int resource_id);
+
+ private:
+ bool Send(IPC::Message* msg);
+
+ // Handler for sending over the paint event to the plugin.
+ void OnPaint(const gfx::Rect& damaged_rect);
+
+ // Updates the shared memory section where windowless plugins paint.
+ void SetWindowlessBuffer(const TransportDIB::Handle& windowless_buffer,
+ const TransportDIB::Handle& background_buffer,
+ const gfx::Rect& window_rect);
+
+ typedef base::hash_map<int, webkit::npapi::WebPluginResourceClient*>
+ ResourceClientMap;
+ ResourceClientMap resource_clients_;
+
+ scoped_refptr<PluginChannel> channel_;
+ int route_id_;
+ NPObject* window_npobject_;
+ NPObject* plugin_element_;
+ webkit::npapi::WebPluginDelegateImpl* delegate_;
+ gfx::Rect damaged_rect_;
+ bool waiting_for_paint_;
+ gfx::NativeViewId containing_window_;
+ // The url of the main frame hosting the plugin.
+ GURL page_url_;
+
+ // Variables used for desynchronized windowless plugin painting. See note in
+ // webplugin_delegate_proxy.h for how this works.
+ bool transparent_;
+#if defined(OS_MACOSX)
+ scoped_ptr<TransportDIB> windowless_dib_;
+ scoped_ptr<TransportDIB> background_dib_;
+ base::mac::ScopedCFTypeRef<CGContextRef> windowless_context_;
+ base::mac::ScopedCFTypeRef<CGContextRef> background_context_;
+ scoped_ptr<WebPluginAcceleratedSurfaceProxy> accelerated_surface_;
+#else
+ scoped_ptr<skia::PlatformCanvas> windowless_canvas_;
+ scoped_ptr<skia::PlatformCanvas> background_canvas_;
+
+#if defined(USE_X11)
+ scoped_ptr<TransportDIB> windowless_dib_;
+ scoped_ptr<TransportDIB> background_dib_;
+ // If we can use SHM pixmaps for windowless plugin painting or not.
+ bool use_shm_pixmap_;
+ // The SHM pixmap for windowless plugin painting.
+ XID windowless_shm_pixmap_;
+#endif
+
+#endif
+
+ // Contains the routing id of the host render view.
+ int host_render_view_routing_id_;
+
+ ScopedRunnableMethodFactory<WebPluginProxy> runnable_method_factory_;
+};
+
+#endif // CONTENT_PLUGIN_WEBPLUGIN_PROXY_H_