diff options
author | initial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-07-26 23:55:29 +0000 |
---|---|---|
committer | initial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-07-26 23:55:29 +0000 |
commit | 09911bf300f1a419907a9412154760efd0b7abc3 (patch) | |
tree | f131325fb4e2ad12c6d3504ab75b16dd92facfed /chrome/plugin | |
parent | 586acc5fe142f498261f52c66862fa417c3d52d2 (diff) | |
download | chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.zip chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.tar.gz chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.tar.bz2 |
Add chrome to the repository.
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@15 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/plugin')
23 files changed, 4378 insertions, 0 deletions
diff --git a/chrome/plugin/SConscript b/chrome/plugin/SConscript new file mode 100644 index 0000000..8dbb9ef --- /dev/null +++ b/chrome/plugin/SConscript @@ -0,0 +1,73 @@ +# Copyright 2008, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+Import('env')
+
+env = env.Clone()
+
+
+
+env.Prepend(
+ CPPPATH = [
+ Dir("#/../third_party/npapi"),
+ Dir("#/../chrome/tools/build/win"),
+ Dir("#/../skia/include"),
+ Dir("#/../skia/include/corecg"),
+ Dir("#/../skia/platform"),
+ Dir("#/.."),
+ ],
+ CPPDEFINES = [
+ "CERT_CHAIN_PARA_HAS_EXTRA_FIELDS",
+ "WIN32_LEAN_AND_MEAN",
+ ],
+ CCFLAGS = [
+ '/TP',
+
+ #'/Wp64',
+
+ '/wd4503',
+ '/wd4819',
+ ],
+)
+
+input_files = [
+ "npobject_proxy.cc",
+ "webplugin_proxy.cc",
+ "webplugin_delegate_stub.cc",
+ "plugin_thread.cc",
+ "plugin_process.cc",
+ "plugin_main.cc",
+ "plugin_channel_base.cc",
+ "plugin_channel.cc",
+ "chrome_plugin_host.cc",
+ "npobject_util.cc",
+ "npobject_stub.cc",
+]
+
+env.StaticLibrary('plugin', input_files)
diff --git a/chrome/plugin/chrome_plugin_host.cc b/chrome/plugin/chrome_plugin_host.cc new file mode 100644 index 0000000..5a3af53 --- /dev/null +++ b/chrome/plugin/chrome_plugin_host.cc @@ -0,0 +1,570 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "chrome/plugin/chrome_plugin_host.h" + +#include "base/file_util.h" +#include "base/message_loop.h" +#include "chrome/common/chrome_constants.h" +#include "chrome/common/chrome_plugin_lib.h" +#include "chrome/common/chrome_plugin_util.h" +#include "chrome/plugin/plugin_process.h" +#include "chrome/plugin/plugin_thread.h" +#include "chrome/plugin/webplugin_proxy.h" +#include "net/base/data_url.h" +#include "webkit/glue/plugins/plugin_instance.h" +#include "webkit/glue/resource_loader_bridge.h" +#include "webkit/glue/resource_type.h" +#include "webkit/glue/webkit_glue.h" + +namespace { + +using webkit_glue::ResourceLoaderBridge; + +// This class manages a network request made by the plugin, handling the +// data as it comes in from the ResourceLoaderBridge and is requested by the +// plugin. +// NOTE: All methods must be called on the Plugin thread. +class PluginRequestHandlerProxy + : public PluginHelper, public ResourceLoaderBridge::Peer { + public: + static PluginRequestHandlerProxy* FromCPRequest(CPRequest* request) { + return ScopableCPRequest::GetData<PluginRequestHandlerProxy*>(request); + } + + PluginRequestHandlerProxy(ChromePluginLib* plugin, + ScopableCPRequest* cprequest) + : PluginHelper(plugin), cprequest_(cprequest), response_data_offset_(0), + completed_(false), sync_(false), read_buffer_(NULL) { + load_flags_ = PluginResponseUtils::CPLoadFlagsToNetFlags(0); + cprequest_->data = this; // see FromCPRequest(). + } + + ~PluginRequestHandlerProxy() { + if (bridge_.get() && !completed_) { + bridge_->Cancel(); + } + } + + // ResourceLoaderBridge::Peer + virtual void OnUploadProgress(uint64 position, uint64 size) { + CPRR_UploadProgressFunc upload_progress = + plugin_->functions().response_funcs->upload_progress; + if (upload_progress) + upload_progress(cprequest_.get(), position, size); + } + + virtual void OnReceivedRedirect(const GURL& new_url) { + plugin_->functions().response_funcs->received_redirect( + cprequest_.get(), new_url.spec().c_str()); + } + + virtual void OnReceivedResponse( + const ResourceLoaderBridge::ResponseInfo& info) { + response_headers_ = info.headers; + plugin_->functions().response_funcs->start_completed( + cprequest_.get(), CPERR_SUCCESS); + } + + virtual void OnReceivedData(const char* data, int len) { + response_data_.append(data, len); + if (read_buffer_) { + // If we had an asynchronous operation pending, read into that buffer + // and inform the plugin. + int rv = Read(read_buffer_, read_buffer_size_); + DCHECK(rv != CPERR_IO_PENDING); + read_buffer_ = NULL; + plugin_->functions().response_funcs->read_completed( + cprequest_.get(), rv); + } + } + + virtual void OnCompletedRequest(const URLRequestStatus& status) { + completed_ = true; + + if (!status.is_success()) { + // TODO(mpcomplete): better error codes + // Inform the plugin, calling the right function depending on whether + // we got the start_completed event or not. + if (response_headers_) { + plugin_->functions().response_funcs->start_completed( + cprequest_.get(), CPERR_FAILURE); + } else { + plugin_->functions().response_funcs->read_completed( + cprequest_.get(), CPERR_FAILURE); + } + } else if (read_buffer_) { + // The plugin was waiting for more data. Inform him we're done. + read_buffer_ = NULL; + plugin_->functions().response_funcs->read_completed( + cprequest_.get(), CPERR_SUCCESS); + } + } + + virtual std::string GetURLForDebugging() { + return cprequest_->url; + } + + void set_extra_headers(const std::string& headers) { + extra_headers_ = headers; + } + void set_load_flags(uint32 flags) { + load_flags_ = flags; + } + void set_sync(bool sync) { + sync_ = sync; + } + void AppendDataToUpload(const char* bytes, int bytes_len) { + upload_content_.push_back(net::UploadData::Element()); + upload_content_.back().SetToBytes(bytes, bytes_len); + } + + void AppendFileToUpload(const std::wstring &filepath) { + AppendFileRangeToUpload(filepath, 0, kuint64max); + } + + void AppendFileRangeToUpload(const std::wstring &filepath, + uint64 offset, uint64 length) { + upload_content_.push_back(net::UploadData::Element()); + upload_content_.back().SetToFilePathRange(filepath, offset, length); + } + + CPError Start() { + bridge_.reset( + PluginThread::GetPluginThread()->resource_dispatcher()->CreateBridge( + cprequest_->method, + GURL(cprequest_->url), + GURL(cprequest_->url), // TODO(jackson): policy url? + GURL(), // TODO(mpcomplete): referrer? + extra_headers_, + load_flags_, + GetCurrentProcessId(), + ResourceType::OBJECT, + false, // TODO (jcampan): mixed-content? + cprequest_->context)); + if (!bridge_.get()) + return CPERR_FAILURE; + + for (size_t i = 0; i < upload_content_.size(); ++i) { + switch (upload_content_[i].type()) { + case net::UploadData::TYPE_BYTES: { + const std::vector<char>& bytes = upload_content_[i].bytes(); + bridge_->AppendDataToUpload(&bytes[0], + static_cast<int>(bytes.size())); + break; + } + case net::UploadData::TYPE_FILE: { + bridge_->AppendFileRangeToUpload( + upload_content_[i].file_path(), + upload_content_[i].file_range_offset(), + upload_content_[i].file_range_length()); + break; + } + default: { + NOTREACHED() << "Unknown UploadData::Element type"; + } + } + } + + if (sync_) { + ResourceLoaderBridge::SyncLoadResponse response; + bridge_->SyncLoad(&response); + response_headers_ = response.headers; + response_data_ = response.data; + completed_ = true; + return response.status.is_success() ? CPERR_SUCCESS : CPERR_FAILURE; + } else { + if (!bridge_->Start(this)) { + bridge_.reset(); + return CPERR_FAILURE; + } + return CPERR_IO_PENDING; + } + } + + int GetResponseInfo(CPResponseInfoType type, void* buf, uint32 buf_size) { + return PluginResponseUtils::GetResponseInfo( + response_headers_, type, buf, buf_size); + } + + int Read(void* buf, uint32 buf_size) { + uint32 avail = + static_cast<uint32>(response_data_.size()) - response_data_offset_; + uint32 count = buf_size; + if (count > avail) + count = avail; + + int rv = CPERR_FAILURE; + if (count) { + // Data is ready now. + memcpy(buf, &response_data_[0] + response_data_offset_, count); + response_data_offset_ += count; + } else if (!completed_) { + read_buffer_ = buf; + read_buffer_size_ = buf_size; + DCHECK(!sync_); + return CPERR_IO_PENDING; + } + + if (response_data_.size() == response_data_offset_) { + // Simple optimization for large requests. Generally the consumer will + // read the data faster than it comes in, so we can clear our buffer + // any time it has all been read. + response_data_.clear(); + response_data_offset_ = 0; + } + + read_buffer_ = NULL; + return count; + } + + private: + scoped_ptr<ScopableCPRequest> cprequest_; + scoped_ptr<ResourceLoaderBridge> bridge_; + std::vector<net::UploadData::Element> upload_content_; + std::string extra_headers_; + uint32 load_flags_; + bool sync_; + + scoped_refptr<net::HttpResponseHeaders> response_headers_; + std::string response_data_; + int response_data_offset_; + bool completed_; + void* read_buffer_; + uint32 read_buffer_size_; +}; + +// +// Generic functions +// + +void STDCALL CPB_SetKeepProcessAlive(CPID id, CPBool keep_alive) { + CHECK(ChromePluginLib::IsPluginThread()); + static bool g_keep_process_alive = false; + bool desired_value = keep_alive ? true : false; // smash to bool + if (desired_value != g_keep_process_alive) { + g_keep_process_alive = desired_value; + if (g_keep_process_alive) + PluginProcess::AddRefProcess(); + else + PluginProcess::ReleaseProcess(); + } +} + +CPError STDCALL CPB_GetCookies(CPID id, CPBrowsingContext context, + const char* url, char** cookies) { + CHECK(ChromePluginLib::IsPluginThread()); + std::string cookies_str; + PluginThread::GetPluginThread()->Send( + new PluginProcessHostMsg_GetCookies(context, GURL(url), &cookies_str)); + *cookies = CPB_StringDup(CPB_Alloc, cookies_str); + return CPERR_SUCCESS; +} + +CPError STDCALL CPB_ShowHtmlDialogModal( + CPID id, CPBrowsingContext context, const char* url, int width, int height, + const char* json_arguments, char** json_retval) { + CHECK(ChromePluginLib::IsPluginThread()); + + WebPluginProxy* webplugin = WebPluginProxy::FromCPBrowsingContext(context); + if (!webplugin) + return CPERR_INVALID_PARAMETER; + + std::string retval_str; + webplugin->ShowModalHTMLDialog( + GURL(url), width, height, json_arguments, &retval_str); + *json_retval = CPB_StringDup(CPB_Alloc, retval_str); + return CPERR_SUCCESS; +} + +CPError STDCALL CPB_ShowHtmlDialog( + CPID id, CPBrowsingContext context, const char* url, int width, int height, + const char* json_arguments, void* plugin_context) { + // TODO(mpcomplete): support non-modal dialogs. + return CPERR_FAILURE; +} + +CPError STDCALL CPB_GetCommandLineArguments( + CPID id, CPBrowsingContext context, const char* url, char** arguments) { + CHECK(ChromePluginLib::IsPluginThread()); + std::string arguments_str; + CPError rv = CPB_GetCommandLineArgumentsCommon(url, &arguments_str); + if (rv == CPERR_SUCCESS) + *arguments = CPB_StringDup(CPB_Alloc, arguments_str); + return rv; +} + +CPBrowsingContext STDCALL CPB_GetBrowsingContextFromNPP(NPP npp) { + if (!npp) + return CPERR_INVALID_PARAMETER; + + NPAPI::PluginInstance* instance = + static_cast<NPAPI::PluginInstance *>(npp->ndata); + WebPluginProxy* webplugin = + static_cast<WebPluginProxy*>(instance->webplugin()); + + return webplugin->GetCPBrowsingContext(); +} + +int STDCALL CPB_GetBrowsingContextInfo( + CPID id, CPBrowsingContext context, CPBrowsingContextInfoType type, + void* buf, uint32 buf_size) { + CHECK(ChromePluginLib::IsPluginThread()); + + switch (type) { + case CPBROWSINGCONTEXT_DATA_DIR_PTR: { + if (buf_size < sizeof(char*)) + return sizeof(char*); + + std::wstring wretval; + PluginThread::GetPluginThread()->Send( + new PluginProcessHostMsg_GetPluginDataDir(&wretval)); + file_util::AppendToPath(&wretval, chrome::kChromePluginDataDirname); + *static_cast<char**>(buf) = CPB_StringDup(CPB_Alloc, WideToUTF8(wretval)); + return CPERR_SUCCESS; + } + case CPBROWSINGCONTEXT_UI_LOCALE_PTR: { + if (buf_size < sizeof(char*)) + return sizeof(char*); + + std::wstring wretval = webkit_glue::GetWebKitLocale(); + *static_cast<char**>(buf) = CPB_StringDup(CPB_Alloc, WideToUTF8(wretval)); + return CPERR_SUCCESS; + } + } + + return CPERR_FAILURE; +} + +CPError STDCALL CPB_AddUICommand(CPID id, int command) { + // Not implemented in the plugin process + return CPERR_FAILURE; +} + +CPError STDCALL CPB_HandleCommand( + CPID id, CPBrowsingContext context, int command, void *data) { + // Not implemented in the plugin process + return CPERR_FAILURE; +} + +// +// Functions related to network interception +// + +void STDCALL CPB_EnableRequestIntercept( + CPID id, const char** schemes, uint32 num_schemes) { + // We ignore requests by the plugin to intercept from this process. That's + // handled in the browser process. +} + +void STDCALL CPRR_ReceivedRedirect(CPRequest* request, const char* new_url) { + NOTREACHED() << "Network interception should not happen in plugin process."; +} + +void STDCALL CPRR_StartCompleted(CPRequest* request, CPError result) { + NOTREACHED() << "Network interception should not happen in plugin process."; +} + +void STDCALL CPRR_ReadCompleted(CPRequest* request, int bytes_read) { + NOTREACHED() << "Network interception should not happen in plugin process."; +} + +void STDCALL CPRR_UploadProgress(CPRequest* request, uint64 pos, uint64 size) { + NOTREACHED() << "Network interception should not happen in plugin process."; +} + +// +// Functions related to serving network requests to the plugin +// + +CPError STDCALL CPB_CreateRequest(CPID id, CPBrowsingContext context, + const char* method, const char* url, + CPRequest** request) { + CHECK(ChromePluginLib::IsPluginThread()); + ChromePluginLib* plugin = ChromePluginLib::FromCPID(id); + CHECK(plugin); + + ScopableCPRequest* cprequest = new ScopableCPRequest(url, method, context); + PluginRequestHandlerProxy* handler = + new PluginRequestHandlerProxy(plugin, cprequest); + + *request = cprequest; + return CPERR_SUCCESS; +} + +CPError STDCALL CPR_StartRequest(CPRequest* request) { + CHECK(ChromePluginLib::IsPluginThread()); + PluginRequestHandlerProxy* handler = + PluginRequestHandlerProxy::FromCPRequest(request); + CHECK(handler); + return handler->Start(); +} + +void STDCALL CPR_EndRequest(CPRequest* request, CPError reason) { + CHECK(ChromePluginLib::IsPluginThread()); + PluginRequestHandlerProxy* handler = + PluginRequestHandlerProxy::FromCPRequest(request); + delete handler; +} + +void STDCALL CPR_SetExtraRequestHeaders(CPRequest* request, + const char* headers) { + CHECK(ChromePluginLib::IsPluginThread()); + PluginRequestHandlerProxy* handler = + PluginRequestHandlerProxy::FromCPRequest(request); + CHECK(handler); + handler->set_extra_headers(headers); +} + +void STDCALL CPR_SetRequestLoadFlags(CPRequest* request, uint32 flags) { + CHECK(ChromePluginLib::IsPluginThread()); + PluginRequestHandlerProxy* handler = + PluginRequestHandlerProxy::FromCPRequest(request); + CHECK(handler); + + if (flags & CPREQUESTLOAD_SYNCHRONOUS) { + handler->set_sync(true); + } + + uint32 net_flags = PluginResponseUtils::CPLoadFlagsToNetFlags(flags); + handler->set_load_flags(net_flags); +} + +void STDCALL CPR_AppendDataToUpload(CPRequest* request, const char* bytes, + int bytes_len) { + CHECK(ChromePluginLib::IsPluginThread()); + PluginRequestHandlerProxy* handler = + PluginRequestHandlerProxy::FromCPRequest(request); + CHECK(handler); + handler->AppendDataToUpload(bytes, bytes_len); +} + +CPError STDCALL CPR_AppendFileToUpload(CPRequest* request, const char* filepath, + uint64 offset, uint64 length) { + CHECK(ChromePluginLib::IsPluginThread()); + PluginRequestHandlerProxy* handler = + PluginRequestHandlerProxy::FromCPRequest(request); + CHECK(handler); + + if (!length) length = kuint64max; + std::wstring wfilepath(UTF8ToWide(filepath)); + handler->AppendFileRangeToUpload(wfilepath, offset, length); + return CPERR_SUCCESS; +} + +int STDCALL CPR_GetResponseInfo(CPRequest* request, CPResponseInfoType type, + void* buf, uint32 buf_size) { + CHECK(ChromePluginLib::IsPluginThread()); + PluginRequestHandlerProxy* handler = + PluginRequestHandlerProxy::FromCPRequest(request); + CHECK(handler); + return handler->GetResponseInfo(type, buf, buf_size); +} + +int STDCALL CPR_Read(CPRequest* request, void* buf, uint32 buf_size) { + CHECK(ChromePluginLib::IsPluginThread()); + PluginRequestHandlerProxy* handler = + PluginRequestHandlerProxy::FromCPRequest(request); + CHECK(handler); + return handler->Read(buf, buf_size); +} + + +CPBool STDCALL CPB_IsPluginProcessRunning(CPID id) { + CHECK(ChromePluginLib::IsPluginThread()); + return true; +} + +CPProcessType STDCALL CPB_GetProcessType(CPID id) { + CHECK(ChromePluginLib::IsPluginThread()); + return CP_PROCESS_PLUGIN; +} + +CPError STDCALL CPB_SendMessage(CPID id, const void *data, uint32 data_len) { + CHECK(ChromePluginLib::IsPluginThread()); + const uint8* data_ptr = static_cast<const uint8*>(data); + std::vector<uint8> v(data_ptr, data_ptr + data_len); + if (!PluginThread::GetPluginThread()->Send( + new PluginProcessHostMsg_PluginMessage(v))) { + return CPERR_FAILURE; + } + return CPERR_SUCCESS; +} + +} // namespace + +CPBrowserFuncs* GetCPBrowserFuncsForPlugin() { + static CPBrowserFuncs browser_funcs; + static CPRequestFuncs request_funcs; + static CPResponseFuncs response_funcs; + static bool initialized = false; + if (!initialized) { + initialized = true; + + browser_funcs.size = sizeof(browser_funcs); + browser_funcs.version = CP_VERSION; + browser_funcs.enable_request_intercept = CPB_EnableRequestIntercept; + browser_funcs.create_request = CPB_CreateRequest; + browser_funcs.get_cookies = CPB_GetCookies; + browser_funcs.alloc = CPB_Alloc; + browser_funcs.free = CPB_Free; + browser_funcs.set_keep_process_alive = CPB_SetKeepProcessAlive; + browser_funcs.show_html_dialog = CPB_ShowHtmlDialog; + browser_funcs.show_html_dialog_modal = CPB_ShowHtmlDialogModal; + browser_funcs.is_plugin_process_running = CPB_IsPluginProcessRunning; + browser_funcs.get_process_type = CPB_GetProcessType; + browser_funcs.send_message = CPB_SendMessage; + browser_funcs.get_browsing_context_from_npp = CPB_GetBrowsingContextFromNPP; + browser_funcs.get_browsing_context_info = CPB_GetBrowsingContextInfo; + browser_funcs.get_command_line_arguments = CPB_GetCommandLineArguments; + browser_funcs.add_ui_command = CPB_AddUICommand; + browser_funcs.handle_command = CPB_HandleCommand; + + browser_funcs.request_funcs = &request_funcs; + browser_funcs.response_funcs = &response_funcs; + + request_funcs.size = sizeof(request_funcs); + request_funcs.start_request = CPR_StartRequest; + request_funcs.end_request = CPR_EndRequest; + request_funcs.set_extra_request_headers = CPR_SetExtraRequestHeaders; + request_funcs.set_request_load_flags = CPR_SetRequestLoadFlags; + request_funcs.append_data_to_upload = CPR_AppendDataToUpload; + request_funcs.get_response_info = CPR_GetResponseInfo; + request_funcs.read = CPR_Read; + request_funcs.append_file_to_upload = CPR_AppendFileToUpload; + + response_funcs.size = sizeof(response_funcs); + response_funcs.received_redirect = CPRR_ReceivedRedirect; + response_funcs.start_completed = CPRR_StartCompleted; + response_funcs.read_completed = CPRR_ReadCompleted; + response_funcs.upload_progress = CPRR_UploadProgress; + } + + return &browser_funcs; +} diff --git a/chrome/plugin/chrome_plugin_host.h b/chrome/plugin/chrome_plugin_host.h new file mode 100644 index 0000000..365f96b --- /dev/null +++ b/chrome/plugin/chrome_plugin_host.h @@ -0,0 +1,38 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef CHROME_PLUGIN_CHROME_PLUGIN_HOST_H__ +#define CHROME_PLUGIN_CHROME_PLUGIN_HOST_H__ + +#include "chrome/common/chrome_plugin_api.h" + +// Returns the table of browser functions for use from the plugin process. +CPBrowserFuncs* GetCPBrowserFuncsForPlugin(); + +#endif // CHROME_PLUGIN_CHROME_PLUGIN_HOST_H__ diff --git a/chrome/plugin/npobject_proxy.cc b/chrome/plugin/npobject_proxy.cc new file mode 100644 index 0000000..2dc8819 --- /dev/null +++ b/chrome/plugin/npobject_proxy.cc @@ -0,0 +1,374 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "chrome/plugin/npobject_proxy.h" + +#include "chrome/common/plugin_messages.h" +#include "chrome/common/win_util.h" +#include "chrome/plugin/npobject_util.h" +#include "chrome/plugin/plugin_channel_base.h" +#include "webkit/glue/webkit_glue.h" +#include "webkit/glue/plugins/plugin_instance.h" + + +struct NPObjectWrapper { + NPObject object; + NPObjectProxy* proxy; +}; + +NPClass NPObjectProxy::npclass_proxy_ = { + 2, + NPObjectProxy::NPAllocate, + NPObjectProxy::NPDeallocate, + NPObjectProxy::NPPInvalidate, + NPObjectProxy::NPHasMethod, + NPObjectProxy::NPInvoke, + NPObjectProxy::NPInvokeDefault, + NPObjectProxy::NPHasProperty, + NPObjectProxy::NPGetProperty, + NPObjectProxy::NPSetProperty, + NPObjectProxy::NPRemoveProperty, + NPObjectProxy::NPNEnumerate +}; + +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; +} + +NPObjectProxy::NPObjectProxy( + PluginChannelBase* channel, + int route_id, + void* npobject_ptr, + HANDLE modal_dialog_event) + : channel_(channel), + route_id_(route_id), + npobject_ptr_(npobject_ptr), + modal_dialog_event_(modal_dialog_event) { + channel_->AddRoute(route_id, this, true); +} + +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, + void* npobject_ptr, + HANDLE modal_dialog_event) { + NPObjectWrapper* obj = reinterpret_cast<NPObjectWrapper*>( + NPN_CreateObject(0, &npclass_proxy_)); + obj->proxy = new NPObjectProxy( + channel, route_id, npobject_ptr, modal_dialog_event); + + 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; +} + +void NPObjectProxy::OnMessageReceived(const IPC::Message& msg) { + NOTREACHED(); +} + +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) { + 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) { + NPObjectProxy* proxy = GetProxy(obj); + if (!proxy) { + return obj->_class->invoke(obj, name, args, arg_count, np_result); + } + + bool result = false; + 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.is_string = true; + } 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, ¶m, false); + args_param.push_back(param); + } + + NPVariant_Param param_result; + NPObjectMsg_Invoke* msg = new NPObjectMsg_Invoke( + proxy->route_id_, is_default, name_param, args_param, ¶m_result, + &result); + + // If we're in the plugin process and this invoke leads to a dialog box, the + // plugin will hang the window hierarchy unless we pump the window message + // queue while waiting for a reply. We need to do this to simulate what + // happens when everything runs in-process (while calling MessageBox window + // messages are pumped). + msg->set_pump_messages_event(proxy->modal_dialog_event_); + + proxy->Send(msg); + + if (!result) + return false; + + CreateNPVariant( + param_result, channel_copy, np_result, proxy->modal_dialog_event_); + return true; +} + +bool NPObjectProxy::NPHasProperty(NPObject *obj, + NPIdentifier name) { + 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)); + + return result; +} + +bool NPObjectProxy::NPGetProperty(NPObject *obj, + NPIdentifier name, + NPVariant *np_result) { + bool result = false; + NPObjectProxy* proxy = GetProxy(obj); + if (!proxy) { + return obj->_class->getProperty(obj, name, np_result); + } + + NPIdentifier_Param name_param; + CreateNPIdentifierParam(name, &name_param); + + NPVariant_Param param; + proxy->Send(new NPObjectMsg_GetProperty( + proxy->route_id(), name_param, ¶m, &result)); + if (!result) + return false; + + CreateNPVariant( + param, proxy->channel(), np_result, proxy->modal_dialog_event_); + + return true; +} + +bool NPObjectProxy::NPSetProperty(NPObject *obj, + NPIdentifier name, + const NPVariant *value) { + bool result = false; + NPObjectProxy* proxy = GetProxy(obj); + if (!proxy) { + return obj->_class->setProperty(obj, name, value); + } + + NPIdentifier_Param name_param; + CreateNPIdentifierParam(name, &name_param); + + NPVariant_Param value_param; + CreateNPVariantParam(*value, proxy->channel(), &value_param, false); + + proxy->Send(new NPObjectMsg_SetProperty( + proxy->route_id(), name_param, value_param, &result)); + + return result; +} + +bool NPObjectProxy::NPRemoveProperty(NPObject *obj, + NPIdentifier name) { + 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)); + + return result; +} + +void NPObjectProxy::NPPInvalidate(NPObject *obj) { + bool result = false; + NPObjectProxy* proxy = GetProxy(obj); + if (!proxy) { + return obj->_class->invalidate(obj); + } + + proxy->Send(new NPObjectMsg_Invalidate(proxy->route_id())); +} + +bool NPObjectProxy::NPNEnumerate(NPObject *obj, + NPIdentifier **value, + uint32_t *count) { + bool result = false; + NPObjectProxy* proxy = GetProxy(obj); + if (!proxy) { + return obj->_class->enumerate(obj, value, count); + } + + std::vector<NPIdentifier_Param> value_param; + proxy->Send(new NPObjectMsg_Enumeration( + proxy->route_id(), &value_param, &result)); + + 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::NPNEvaluate(NPP npp, + NPObject *obj, + NPString *script, + NPVariant *result_var) { + bool result = false; + NPObjectProxy* proxy = GetProxy(obj); + if (!proxy) { + return false; + } + + 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, + &result_param, + &result); + + // Please refer to the comments in NPObjectProxy::NPInvokePrivate for + // the reasoning behind setting the pump messages event in the sync message. + msg->set_pump_messages_event(proxy->modal_dialog_event_); + proxy->Send(msg); + if (!result) + return false; + + CreateNPVariant( + result_param, proxy->channel(), result_var, proxy->modal_dialog_event_); + return true; +} + +void NPObjectProxy::NPNSetException(NPObject *obj, + const NPUTF8 *message) { + NPObjectProxy* proxy = GetProxy(obj); + if (!proxy) { + return; + } + + NPVariant_Param result_param; + std::string message_str(message); + + proxy->Send(new NPObjectMsg_SetException(proxy->route_id(), message_str)); +} diff --git a/chrome/plugin/npobject_proxy.h b/chrome/plugin/npobject_proxy.h new file mode 100644 index 0000000..b75aa1c --- /dev/null +++ b/chrome/plugin/npobject_proxy.h @@ -0,0 +1,140 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// A proxy for NPObject that sends all calls to the object to an NPObjectStub +// running in a different process. + +#ifndef CHROME_PLUGIN_NPOBJECT_PROXY_H__ +#define CHROME_PLUGIN_NPOBJECT_PROXY_H__ + +#include "base/ref_counted.h" +#include "bindings/npruntime.h" +#include "chrome/common/ipc_channel.h" + +class PluginChannelBase; +struct NPObject; +struct NPVariant_Param; + +// 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: + ~NPObjectProxy(); + + static NPObject* Create(PluginChannelBase* channel, + int route_id, + void* npobject_ptr, + HANDLE modal_dialog_event); + + // IPC::Message::Sender implementation: + bool Send(IPC::Message* msg); + int route_id() { return route_id_; } + PluginChannelBase* channel() { return channel_; } + + // Returns the real NPObject's pointer (obviously only valid in the other + // process). + void* npobject_ptr() { return npobject_ptr_; } + + // The next 8 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); + + // The next two functions are only called on NPObjects from the browser. + static bool NPNEvaluate(NPP npp, + NPObject *obj, + NPString *script, + NPVariant *result); + + static void NPNSetException(NPObject *obj, + const NPUTF8 *message); + + 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_; } + + private: + NPObjectProxy(PluginChannelBase* channel, + int route_id, + void* npobject_ptr, + HANDLE modal_dialog_event); + + // IPC::Channel::Listener implementation: + void OnMessageReceived(const IPC::Message& msg); + 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_; + + int route_id_; + void* npobject_ptr_; + scoped_refptr<PluginChannelBase> channel_; + HANDLE modal_dialog_event_; +}; + +#endif // CHROME_PLUGIN_NPOBJECT_PROXY_H__ diff --git a/chrome/plugin/npobject_stub.cc b/chrome/plugin/npobject_stub.cc new file mode 100644 index 0000000..3ec6ca5 --- /dev/null +++ b/chrome/plugin/npobject_stub.cc @@ -0,0 +1,320 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "chrome/plugin/npobject_stub.h" + +#include "bindings/npapi.h" +#include "bindings/npruntime.h" +#include "chrome/common/plugin_messages.h" +#include "chrome/plugin/npobject_util.h" +#include "chrome/plugin/plugin_channel_base.h" +#include "chrome/renderer/webplugin_delegate_proxy.h" + +NPObjectStub::NPObjectStub( + NPObject* npobject, PluginChannelBase* channel, int route_id) + : channel_(channel), + npobject_(npobject), + route_id_(route_id), + valid_(true), + web_plugin_delegate_proxy_(NULL) { + channel_->AddRoute(route_id, this, true); + + // We retain the object just as PluginHost does if everything was in-process. + NPN_RetainObject(npobject_); +} + +NPObjectStub::~NPObjectStub() { + if (web_plugin_delegate_proxy_) + web_plugin_delegate_proxy_->DropWindowScriptObject(); + + channel_->RemoveRoute(route_id_); + if (npobject_ && valid_) + NPN_ReleaseObject(npobject_); +} + +bool NPObjectStub::Send(IPC::Message* msg) { + return channel_->Send(msg); +} + +void NPObjectStub::OnMessageReceived(const IPC::Message& msg) { + if (!valid_) { + 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; + } + + 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(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_Evaluate, OnEvaluate); + IPC_MESSAGE_HANDLER(NPObjectMsg_SetException, OnSetException); + IPC_MESSAGE_UNHANDLED_ERROR() + IPC_END_MESSAGE_MAP() +} + +void NPObjectStub::OnChannelError() { + // When the plugin process is shutting down, all the NPObjectStubs + // destructors are called. However the plugin dll might have already + // been released, in which case the NPN_ReleaseObject will cause a crash. + npobject_ = NULL; + 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 = NPN_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); + + int arg_count = static_cast<int>(args.size()); + NPVariant* args_var = new NPVariant[arg_count]; + for (int i = 0; i < arg_count; ++i) + CreateNPVariant(args[i], local_channel, &(args_var[i]), NULL); + + 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 = NPN_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 = NPN_Invoke( + 0, npobject_, id, args_var, arg_count, &result_var); + } + } + + for (int i = 0; i < arg_count; ++i) + NPN_ReleaseVariantValue(&(args_var[i])); + + delete[] args_var; + + CreateNPVariantParam(result_var, local_channel, &result_param, true); + 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 = NPN_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 = NPN_GetProperty(0, npobject_, id, &result_var); + } + + CreateNPVariantParam(result_var, channel_, property, true); +} + +void NPObjectStub::OnSetProperty(const NPIdentifier_Param& name, + const NPVariant_Param& property, + bool* result) { + NPVariant result_var; + VOID_TO_NPVARIANT(result_var); + NPIdentifier id = CreateNPIdentifier(name); + NPVariant property_var; + CreateNPVariant(property, channel_, &property_var, NULL); + + if (IsPluginProcess()) { + if (npobject_->_class->setProperty) { + *result = npobject_->_class->setProperty(npobject_, id, &property_var); + } else { + *result = false; + } + } else { + *result = NPN_SetProperty(0, npobject_, id, &property_var); + } + + NPN_ReleaseVariantValue(&property_var); +} + +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 = NPN_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 = NPN_Enumerate(0, npobject_, &value_np, &count); + } else { + if (!npobject_->_class->enumerate) { + *result = false; + return; + } + + *result = npobject_->_class->enumerate(npobject_, &value_np, &count); + } + + if (!*result) + return; + + for (unsigned int i = 0; i < count; ++i) { + NPIdentifier_Param param; + CreateNPIdentifierParam(value_np[i], ¶m); + value->push_back(param); + } + + NPN_MemFree(value_np); +} + +void NPObjectStub::OnEvaluate(const std::string& script, + 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 = NPN_Evaluate(0, npobject_, &script_string, &result_var); + + NPVariant_Param result_param; + CreateNPVariantParam(result_var, local_channel, &result_param, true); + NPObjectMsg_Evaluate::WriteReplyParams(reply_msg, result_param, return_value); + local_channel->Send(reply_msg); +} + +void NPObjectStub::OnSetException(const std::string& message) { + if (IsPluginProcess()) { + NOTREACHED() << "Should only be called on NPObjects in the renderer"; + return; + } + + NPN_SetException(npobject_, message.c_str()); +} diff --git a/chrome/plugin/npobject_stub.h b/chrome/plugin/npobject_stub.h new file mode 100644 index 0000000..4d0bf2c --- /dev/null +++ b/chrome/plugin/npobject_stub.h @@ -0,0 +1,106 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// A class that receives IPC messages from an NPObjectProxy and calls the real +// NPObject. + +#ifndef CHROME_PLUGIN_NPOBJECT_STUB_H__ +#define CHROME_PLUGIN_NPOBJECT_STUB_H__ + +#include "base/ref_counted.h" +#include "chrome/common/ipc_channel.h" + +class PluginChannelBase; +class WebPluginDelegateProxy; +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: + NPObjectStub(NPObject* npobject, PluginChannelBase* channel, int route_id); + ~NPObjectStub(); + + // IPC::Message::Sender implementation: + 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 set_invalid() { valid_ = false; } + void set_proxy(WebPluginDelegateProxy* proxy) { + web_plugin_delegate_proxy_ = proxy; + } + + private: + // IPC::Channel::Listener implementation: + void OnMessageReceived(const IPC::Message& message); + 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, + bool* result); + void OnRemoveProperty(const NPIdentifier_Param& name, + bool* result); + void OnInvalidate(); + void OnEnumeration(std::vector<NPIdentifier_Param>* value, + bool* result); + void OnEvaluate(const std::string& script, IPC::Message* reply_msg); + void OnSetException(const std::string& message); + + private: + NPObject* npobject_; + int route_id_; + scoped_refptr<PluginChannelBase> channel_; + + // These variables are used to ensure that the window script object is not + // called after the plugin widget has gone away, as the frame manually + // deallocates it and ignores the refcount to avoid leaks. + bool valid_; + WebPluginDelegateProxy* web_plugin_delegate_proxy_; +}; + +#endif // CHROME_PLUGIN_NPOBJECT_STUB_H__ diff --git a/chrome/plugin/npobject_util.cc b/chrome/plugin/npobject_util.cc new file mode 100644 index 0000000..fa6f68e --- /dev/null +++ b/chrome/plugin/npobject_util.cc @@ -0,0 +1,275 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "chrome/plugin/npobject_util.h" + +#include "chrome/common/plugin_messages.h" +#include "chrome/common/win_util.h" +#include "chrome/plugin/npobject_proxy.h" +#include "chrome/plugin/plugin_channel_base.h" +#include "webkit/glue/plugins/nphostapi.h" +#include "webkit/glue/plugins/plugin_host.h" +#include "webkit/glue/webkit_glue.h" + +// 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) { + return NPObjectProxy::NPNSetException(obj, message); +} + +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(); + NPAPI::PluginHost::Singleton()->PatchNPNetscapeFuncs(funcs); +} + +bool IsPluginProcess() { + return g_plugin_process; +} + +void CreateNPIdentifierParam(NPIdentifier id, NPIdentifier_Param* param) { + param->is_string = NPN_IdentifierIsString(id); + if (param->is_string) { + NPUTF8* utf8 = NPN_UTF8FromIdentifier(id); + param->string = utf8; + NPN_MemFree(utf8); + } else { + param->number = NPN_IntFromIdentifier(id); + } +} + +NPIdentifier CreateNPIdentifier(const NPIdentifier_Param& param) { + if (param.is_string) { + return NPN_GetStringIdentifier(param.string.c_str()); + } else { + return NPN_GetIntIdentifier(param.number); + } +} + +void CreateNPVariantParam(const NPVariant& variant, + PluginChannelBase* channel, + NPVariant_Param* param, + bool release) { + 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 = std::string(variant.value.stringValue.UTF8Characters, + variant.value.stringValue.UTF8Length); + } + break; + case NPVariantType_Object: + { + if (variant.value.objectValue->_class == NPObjectProxy::npclass()) { + param->type = NPVARIANT_PARAM_OBJECT_POINTER; + param->npobject_pointer = + NPObjectProxy::GetProxy(variant.value.objectValue)->npobject_ptr(); + // Don't release, because our original variant is the same as our proxy. + release = false; + } else { + // 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_OBJECT_ROUTING_ID; + int route_id = channel->GenerateRouteID(); + NPObjectStub* object_stub = new NPObjectStub( + variant.value.objectValue, channel, route_id); + param->npobject_routing_id = route_id; + param->npobject_pointer = variant.value.objectValue; + } + break; + } + default: + NOTREACHED(); + } + + if (release) + NPN_ReleaseVariantValue(const_cast<NPVariant*>(&variant)); +} + +void CreateNPVariant(const NPVariant_Param& param, + PluginChannelBase* channel, + NPVariant* result, + HANDLE modal_dialog_event) { + 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 *>(_strdup(param.string_value.c_str())); + result->value.stringValue.UTF8Length = + static_cast<int>(param.string_value.size()); + break; + case NPVARIANT_PARAM_OBJECT_ROUTING_ID: + result->type = NPVariantType_Object; + result->value.objectValue = NPObjectProxy::Create(channel, + param.npobject_routing_id, + param.npobject_pointer, + modal_dialog_event); + break; + case NPVARIANT_PARAM_OBJECT_POINTER: + result->type = NPVariantType_Object; + result->value.objectValue = static_cast<NPObject*>(param.npobject_pointer); + NPN_RetainObject(result->value.objectValue); + break; + default: + NOTREACHED(); + } +} diff --git a/chrome/plugin/npobject_util.h b/chrome/plugin/npobject_util.h new file mode 100644 index 0000000..4a0694d --- /dev/null +++ b/chrome/plugin/npobject_util.h @@ -0,0 +1,84 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Helper functions that are used by the NPObject proxy and stub. + +#ifndef CHROME_PLUGIN_NPOBJECT_UTIL_H__ +#define CHROME_PLUGIN_NPOBJECT_UTIL_H__ + +#include <windows.h> +#include "chrome/plugin/npobject_stub.h" + +struct _NPVariant; +typedef _NPVariant NPVariant; +class NPObjectProxy; +class PluginChannelBase; +struct NPIdentifier_Param; +struct NPVariant_Param; +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); + +// Creates an NPVariant from the marshalled object. +void CreateNPVariant(const NPVariant_Param& param, + PluginChannelBase* channel, + NPVariant* result, + HANDLE modal_dialog_event); + +// Given a plugin's HWND, returns an event associated with the WebContents +// 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 // CHROME_PLUGIN_NPOBJECT_UTIL_H__ diff --git a/chrome/plugin/plugin.vcproj b/chrome/plugin/plugin.vcproj new file mode 100644 index 0000000..008a702 --- /dev/null +++ b/chrome/plugin/plugin.vcproj @@ -0,0 +1,231 @@ +<?xml version="1.0" encoding="Windows-1252"?> +<VisualStudioProject + ProjectType="Visual C++" + Version="8.00" + Name="plugin" + ProjectGUID="{20A560A0-2CD0-4D9E-A58B-1F24B99C087A}" + RootNamespace="plugin" + Keyword="Win32Proj" + > + <Platforms> + <Platform + Name="Win32" + /> + </Platforms> + <ToolFiles> + </ToolFiles> + <Configurations> + <Configuration + Name="Debug|Win32" + ConfigurationType="4" + InheritedPropertySheets="$(SolutionDir)..\build\common.vsprops;$(SolutionDir)..\build\debug.vsprops;$(SolutionDir)..\skia\using_skia.vsprops;..\tools\build\win\precompiled.vsprops;$(SolutionDir)..\third_party\npapi\using_npapi.vsprops" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLibrarianTool" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCPostBuildEventTool" + /> + </Configuration> + <Configuration + Name="Release|Win32" + ConfigurationType="4" + InheritedPropertySheets="$(SolutionDir)..\build\common.vsprops;$(SolutionDir)..\build\release.vsprops;$(SolutionDir)..\third_party\npapi\using_npapi.vsprops;$(SolutionDir)..\skia\using_skia.vsprops" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLibrarianTool" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCPostBuildEventTool" + /> + </Configuration> + </Configurations> + <References> + </References> + <Files> + <Filter + Name="NPObject" + > + <File + RelativePath=".\npobject_proxy.cc" + > + </File> + <File + RelativePath=".\npobject_proxy.h" + > + </File> + <File + RelativePath=".\npobject_stub.cc" + > + </File> + <File + RelativePath=".\npobject_stub.h" + > + </File> + <File + RelativePath=".\npobject_util.cc" + > + </File> + <File + RelativePath=".\npobject_util.h" + > + </File> + </Filter> + <File + RelativePath=".\chrome_plugin_host.cc" + > + </File> + <File + RelativePath=".\chrome_plugin_host.h" + > + </File> + <File + RelativePath=".\plugin_channel.cc" + > + </File> + <File + RelativePath=".\plugin_channel.h" + > + </File> + <File + RelativePath=".\plugin_channel_base.cc" + > + </File> + <File + RelativePath=".\plugin_channel_base.h" + > + </File> + <File + RelativePath=".\plugin_main.cc" + > + </File> + <File + RelativePath=".\plugin_process.cc" + > + </File> + <File + RelativePath=".\plugin_process.h" + > + </File> + <File + RelativePath=".\plugin_thread.cc" + > + </File> + <File + RelativePath=".\plugin_thread.h" + > + </File> + <File + RelativePath="..\tools\build\win\precompiled.cc" + > + <FileConfiguration + Name="Debug|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="1" + /> + </FileConfiguration> + </File> + <File + RelativePath="..\tools\build\win\precompiled.h" + > + </File> + <File + RelativePath=".\webplugin_delegate_stub.cc" + > + </File> + <File + RelativePath=".\webplugin_delegate_stub.h" + > + </File> + <File + RelativePath=".\webplugin_proxy.cc" + > + </File> + <File + RelativePath=".\webplugin_proxy.h" + > + </File> + </Files> + <Globals> + </Globals> +</VisualStudioProject> diff --git a/chrome/plugin/plugin_channel.cc b/chrome/plugin/plugin_channel.cc new file mode 100644 index 0000000..e21f5a9 --- /dev/null +++ b/chrome/plugin/plugin_channel.cc @@ -0,0 +1,136 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include <windows.h> + +#include "chrome/plugin/plugin_channel.h" + +#include "chrome/common/plugin_messages.h" +#include "base/string_util.h" +#include "chrome/plugin/plugin_thread.h" +#include "chrome/plugin/plugin_process.h" + +PluginChannel* PluginChannel::GetPluginChannel( + int process_id, HANDLE renderer_handle, MessageLoop* ipc_message_loop) { + // map renderer's process id to a (single) channel to that process + std::wstring channel_name = StringPrintf( + L"%d.r%d", GetCurrentProcessId(), process_id); + + PluginChannelBase* result = PluginChannelBase::GetChannel( + channel_name, + IPC::Channel::MODE_SERVER, + ClassFactory, + ipc_message_loop, + false); + + PluginChannel* channel = static_cast<PluginChannel*>(result); + if (channel && !channel->renderer_handle()) + channel->renderer_handle_.Set(renderer_handle); + + return channel; +} + +PluginChannel::PluginChannel() : in_send_(0) { + SendUnblockingOnlyDuringDispatch(); + PluginProcess::AddRefProcess(); +} + +PluginChannel::~PluginChannel() { + PluginProcess::ReleaseProcess(); +} + +bool PluginChannel::Send(IPC::Message* msg) { + in_send_++; + bool result = PluginChannelBase::Send(msg); + in_send_--; + return result; +} + +void PluginChannel::OnControlMessageReceived(const IPC::Message& msg) { + 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_UNHANDLED_ERROR() + IPC_END_MESSAGE_MAP() +} + +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, false); + 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) { + plugin_stubs_.erase(plugin_stubs_.begin() + i); + RemoveRoute(instance_id); + Send(reply_msg); + return; + } + } + + NOTREACHED() << "Couldn't find WebPluginDelegateStub to destroy"; +} + +void PluginChannel::OnGenerateRouteID(int* route_id) { + *route_id = GenerateRouteID(); +} + +int PluginChannel::GenerateRouteID() { + static LONG last_id = 0; + return InterlockedIncrement(&last_id); +} + +void PluginChannel::OnChannelError() { + renderer_handle_.Set(NULL); + 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(); +}
\ No newline at end of file diff --git a/chrome/plugin/plugin_channel.h b/chrome/plugin/plugin_channel.h new file mode 100644 index 0000000..0324bbb --- /dev/null +++ b/chrome/plugin/plugin_channel.h @@ -0,0 +1,84 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef CHROME_PLUGIN_PLUGIN_CHANNEL_H__ +#define CHROME_PLUGIN_PLUGIN_CHANNEL_H__ + +#include <vector> +#include "base/scoped_handle.h" +#include "chrome/plugin/plugin_channel_base.h" +#include "chrome/plugin/webplugin_delegate_stub.h" + +// 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: + // renderer_handle is the the handle to the renderer process requesting the + // channel. The handle has to be valid in the context of the plugin process. + static PluginChannel* GetPluginChannel( + int process_id, HANDLE renderer_handle, MessageLoop* ipc_message_loop); + + ~PluginChannel(); + + virtual bool Send(IPC::Message* msg); + + HANDLE renderer_handle() { return renderer_handle_.Get(); } + int GenerateRouteID(); + + bool in_send() { return in_send_ != 0; } + + protected: + // IPC::Channel::Listener implementation: + virtual void OnChannelError(); + + virtual void CleanUp(); + + private: + // Called on the plugin thread + PluginChannel(); + + void 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); + + std::vector<scoped_refptr<WebPluginDelegateStub>> plugin_stubs_; + + // Handle to the renderer process who is on the other side of the channel. + ScopedHandle renderer_handle_; + + int in_send_; // Tracks if we're in a Send call. + + DISALLOW_EVIL_CONSTRUCTORS(PluginChannel); +}; + +#endif // CHROME_PLUGIN_PLUGIN_CHANNEL_H__ diff --git a/chrome/plugin/plugin_channel_base.cc b/chrome/plugin/plugin_channel_base.cc new file mode 100644 index 0000000..1cb3111 --- /dev/null +++ b/chrome/plugin/plugin_channel_base.cc @@ -0,0 +1,216 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include <hash_map> +#include <windows.h> + +#include "chrome/plugin/plugin_channel_base.h" + +#include "chrome/common/ipc_sync_message.h" + +typedef stdext::hash_map<std::wstring, scoped_refptr<PluginChannelBase> > + PluginChannelMap; + +static PluginChannelMap g_plugin_channels_; + + +PluginChannelBase* PluginChannelBase::GetChannel( + const std::wstring& channel_name, IPC::Channel::Mode mode, + PluginChannelFactory factory, MessageLoop* ipc_message_loop, + bool create_pipe_now) { + scoped_refptr<PluginChannelBase> channel; + + PluginChannelMap::const_iterator iter = g_plugin_channels_.find(channel_name); + if (iter == g_plugin_channels_.end()) { + channel = factory(); + } else { + channel = iter->second; + } + + DCHECK(channel != NULL); + + if (!channel->channel_valid()) { + channel->channel_name_ = channel_name; + channel->mode_ = mode; + if (channel->Init(ipc_message_loop, create_pipe_now)) { + g_plugin_channels_[channel_name] = channel; + } else { + channel = NULL; + } + } + + return channel; +} + +PluginChannelBase::PluginChannelBase() + : plugin_count_(0), + peer_pid_(0), + in_remove_route_(false), + channel_valid_(false), + in_dispatch_(0), + send_unblocking_only_during_dispatch_(false) { +} + +PluginChannelBase::~PluginChannelBase() { +} + +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(); +} + +bool PluginChannelBase::Init(MessageLoop* ipc_message_loop, + bool create_pipe_now) { + channel_.reset(new IPC::SyncChannel(channel_name_, mode_, this, + ipc_message_loop, create_pipe_now)); + channel_valid_ = true; + return true; +} + +bool PluginChannelBase::Send(IPC::Message* message) { + if (!channel_.get()) { + delete message; + return false; + } + + if (send_unblocking_only_during_dispatch_ && in_dispatch_ == 0 && + message->is_sync()) { + message->set_unblock(false); + } + + return channel_->Send(message); +} + +int PluginChannelBase::Count() { + return static_cast<int>(g_plugin_channels_.size()); +} + +void 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_. + scoped_refptr<PluginChannelBase> me(this); + + in_dispatch_++; + if (message.routing_id() == MSG_ROUTING_CONTROL) { + OnControlMessageReceived(message); + } else { + bool routed = router_.RouteMessage(message); + if (!routed && 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); + } + } + in_dispatch_--; +} + +void PluginChannelBase::OnChannelConnected(int32 peer_pid) { + peer_pid_ = peer_pid; +} + +void PluginChannelBase::AddRoute(int route_id, + IPC::Channel::Listener* listener, + bool npobject) { + if (npobject) { + npobject_listeners_[route_id] = listener; + } 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_) + npobject_listeners_.erase(iter); + + return; + } + + plugin_count_--; + DCHECK(plugin_count_ >= 0); + + if (!plugin_count_) { + ListenerMap::iterator npobj_iter = npobject_listeners_.begin(); + in_remove_route_ = true; + while (npobj_iter != npobject_listeners_.end()) { + npobj_iter->second->OnChannelError(); + npobj_iter++; + } + in_remove_route_ = false; + + PluginChannelMap::iterator iter = g_plugin_channels_.begin(); + while (iter != g_plugin_channels_.end()) { + if (iter->second == this) { + g_plugin_channels_.erase(iter); + return; + } + + iter++; + } + + NOTREACHED(); + } +} + +void PluginChannelBase::OnControlMessageReceived(const IPC::Message& msg) { + NOTREACHED() << "should override in subclass if you care about control messages"; +} + +void PluginChannelBase::OnChannelError() { + channel_valid_ = false; +} + +void PluginChannelBase::SendUnblockingOnlyDuringDispatch() { + send_unblocking_only_during_dispatch_ = true; +} diff --git a/chrome/plugin/plugin_channel_base.h b/chrome/plugin/plugin_channel_base.h new file mode 100644 index 0000000..12565e1 --- /dev/null +++ b/chrome/plugin/plugin_channel_base.h @@ -0,0 +1,144 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef CHROME_WEBKIT_GLUE_PLUGIN_CHANNEL_BASE_H__ +#define CHROME_WEBKIT_GLUE_PLUGIN_CHANNEL_BASE_H__ + +#include <hash_map> +#include <string> + +#include "base/basictypes.h" +#include "base/message_loop.h" +#include "base/ref_counted.h" +#include "base/scoped_ptr.h" +#include "chrome/common/ipc_sync_channel.h" +#include "chrome/common/message_router.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: + virtual ~PluginChannelBase(); + + // 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, bool npobject); + void RemoveRoute(int route_id); + + // IPC::Message::Sender implementation: + virtual bool Send(IPC::Message* msg); + + int peer_pid() { return peer_pid_; } + std::wstring channel_name() const { return channel_name_; } + + // 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_; + } + + static void CleanupChannels(); + + protected: + typedef PluginChannelBase* (*PluginChannelFactory)(); + + // 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 std::wstring& channel_name, IPC::Channel::Mode mode, + PluginChannelFactory factory, MessageLoop* ipc_message_loop, + bool create_pipe_now); + + // Called on the worker thread + PluginChannelBase(); + + virtual void CleanUp() { } + + // Implemented by derived classes to handle control messages + virtual void OnControlMessageReceived(const IPC::Message& msg); + + // IPC::Channel::Listener implementation: + virtual void OnMessageReceived(const IPC::Message& msg); + virtual void OnChannelConnected(int32 peer_pid); + virtual void OnChannelError(); + + // If this is set, sync messages that are sent will only unblock the receiver + // if this channel is in the middle of a dispatch. + void SendUnblockingOnlyDuringDispatch(); + + virtual bool Init(MessageLoop* ipc_message_loop, bool create_pipe_now); + + scoped_ptr<IPC::SyncChannel> channel_; + + private: + + IPC::Channel::Mode mode_; + std::wstring channel_name_; + 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 stdext::hash_map<int, IPC::Channel::Listener*> 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_; + + // If true, sync messages will only be marked as unblocking if the channel is + // in the middle of dispatching a message. + bool send_unblocking_only_during_dispatch_; + int in_dispatch_; + + DISALLOW_EVIL_CONSTRUCTORS(PluginChannelBase); +}; + +#endif // CHROME_WEBKIT_GLUE_PLUGIN_CHANNEL_BASE_H__ diff --git a/chrome/plugin/plugin_main.cc b/chrome/plugin/plugin_main.cc new file mode 100644 index 0000000..7f9c282 --- /dev/null +++ b/chrome/plugin/plugin_main.cc @@ -0,0 +1,100 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "base/command_line.h" +#include "base/message_loop.h" +#include "chrome/common/chrome_constants.h" +#include "chrome/common/chrome_switches.h" +#include "chrome/common/logging_chrome.h" +#include "chrome/common/win_util.h" +#include "chrome/plugin/plugin_process.h" +#include "chrome/test/injection_test_dll.h" +#include "sandbox/src/sandbox.h" + +// mainline routine for running as the plugin process +int PluginMain(CommandLine &parsed_command_line, int show_command, + sandbox::TargetServices* target_services) { + CoInitialize(NULL); + DLOG(INFO) << "Started plugin with " << + parsed_command_line.command_line_string(); + + HMODULE sandbox_test_module = NULL; + bool no_sandbox = parsed_command_line.HasSwitch(switches::kNoSandbox) || + !parsed_command_line.HasSwitch(switches::kSafePlugins); + if (target_services && !no_sandbox) { + // The command line might specify a test dll to load. + if (parsed_command_line.HasSwitch(switches::kTestSandbox)) { + std::wstring test_dll_name = + parsed_command_line.GetSwitchValue(switches::kTestSandbox); + sandbox_test_module = LoadLibrary(test_dll_name.c_str()); + DCHECK(sandbox_test_module); + } + } + + if (parsed_command_line.HasSwitch(switches::kPluginStartupDialog)) { + std::wstring title = chrome::kBrowserAppName; + title += L" plugin"; // makes attaching to process easier + win_util::MessageBox(NULL, L"plugin starting...", title, + MB_OK | MB_SETFOREGROUND); + } + + std::wstring channel_name = + parsed_command_line.GetSwitchValue(switches::kProcessChannelID); + std::wstring plugin_path = + parsed_command_line.GetSwitchValue(switches::kPluginPath); + if (PluginProcess::GlobalInit(channel_name, plugin_path)) { + if (!no_sandbox && target_services) { + target_services->LowerToken(); + } + + if (sandbox_test_module) { + RunRendererTests run_security_tests = + reinterpret_cast<RunPluginTests>(GetProcAddress(sandbox_test_module, + kPluginTestCall)); + DCHECK(run_security_tests); + if (run_security_tests) { + int test_count = 0; + DLOG(INFO) << "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(); + } + } + + // Load the accelerator table from the browser executable and tell the + // message loop to use it when translating messages. + MessageLoop::current()->Run(); + } + PluginProcess::GlobalCleanup(); + + CoUninitialize(); + return 0; +} diff --git a/chrome/plugin/plugin_process.cc b/chrome/plugin/plugin_process.cc new file mode 100644 index 0000000..53c0a3a --- /dev/null +++ b/chrome/plugin/plugin_process.cc @@ -0,0 +1,122 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include <windows.h> +#include "chrome/plugin/plugin_process.h" + +#include "base/basictypes.h" +#include "base/scoped_handle.h" +#include "chrome/common/ipc_channel.h" +#include "chrome/common/ipc_message_utils.h" +#include "chrome/common/plugin_messages.h" +#include "chrome/common/render_messages.h" +#include "webkit/glue/webkit_glue.h" + +// Custom factory to allow us to pass additional ctor arguments. +class PluginProcessFactory : public ChildProcessFactoryInterface { + public: + explicit PluginProcessFactory(const std::wstring& plugin_path) + : plugin_path_(plugin_path) { + } + + virtual ChildProcess* Create(const std::wstring& channel_name) { + return new PluginProcess(channel_name, plugin_path_); + } + + const std::wstring& plugin_path_; +}; + +// How long to wait after there are no more plugin instances before killing the +// process. +static const int kProcessShutdownDelayMs = 10 * 1000; + +// No AddRef required when using PluginProcess with RunnableMethod. This is +// okay since the lifetime of the PluginProcess is always greater than the +// lifetime of PluginThread because it's a member variable. +template <> struct RunnableMethodTraits<PluginProcess> { + static void RetainCallee(PluginProcess*) {} + static void ReleaseCallee(PluginProcess*) {} +}; + +PluginProcess::PluginProcess(const std::wstring& channel_name, + const std::wstring& plugin_path) : + plugin_path_(plugin_path), +#pragma warning(suppress: 4355) // Okay to pass "this" here. + plugin_thread_(this, channel_name) { +} + +PluginProcess::~PluginProcess() { +} + +bool PluginProcess::GlobalInit(const std::wstring &channel_name, + const std::wstring &plugin_path) { + PluginProcessFactory factory(plugin_path); + return ChildProcess::GlobalInit(channel_name, &factory); +} + + +// Note: may be called on any thread +void PluginProcess::OnFinalRelease() { + // We override this to have the process linger for a few seconds to + // better accomdate back/forth navigation. This avoids shutting down and + // immediately starting a new plugin process. If a new channel is + // opened in the interim, the current process will not be shutdown. + main_thread_loop_->PostDelayedTask(FROM_HERE, NewRunnableMethod( + this, &PluginProcess::OnProcessShutdownTimeout), + kProcessShutdownDelayMs); +} + +void PluginProcess::OnProcessShutdownTimeout() { + if (GetProcessRefcount() == 0) { + // The plugin process shutdown sequence is a request response based + // mechanism, where we send out an initial feeler request to the plugin + // process host instance in the browser to verify if it is ok to shutdown + // the plugin process. The browser then sends back a response indicating + // whether it is ok to shutdown. + plugin_thread_.Send(new PluginProcessHostMsg_ShutdownRequest); + } +} + +// static +void PluginProcess::ShutdownProcessResponse(bool ok_to_shutdown) { + if (ok_to_shutdown) { + PluginProcess* plugin_process = + static_cast<PluginProcess*>(child_process_); + DCHECK(plugin_process); + plugin_process->Shutdown(); + } +} + +void PluginProcess::BrowserShutdown() { + ShutdownProcessResponse(true); +} + +void PluginProcess::Shutdown() { + ChildProcess::OnFinalRelease(); +} diff --git a/chrome/plugin/plugin_process.h b/chrome/plugin/plugin_process.h new file mode 100644 index 0000000..e43a1ec --- /dev/null +++ b/chrome/plugin/plugin_process.h @@ -0,0 +1,76 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef CHROME_PLUGIN_PLUGIN_PROCESS_H__ +#define CHROME_PLUGIN_PLUGIN_PROCESS_H__ + +#include "chrome/common/child_process.h" +#include "chrome/plugin/plugin_thread.h" + +// Represents the plugin end of the renderer<->plugin connection. The +// opposite end is the PluginProcessHost. This is a singleton object for +// each plugin. +class PluginProcess : public ChildProcess { + public: + static bool GlobalInit(const std::wstring& channel_name, + const std::wstring& plugin_path); + + // Invoked with the response from the browser indicating whether it is + // ok to shutdown the plugin process. + static void ShutdownProcessResponse(bool ok_to_shutdown); + + // Invoked when the browser is shutdown. This ensures that the plugin + // process does not hang around waiting for future invocations + // from the browser. + static void BrowserShutdown(); + + // File path of the plugin dll this process hosts. + const std::wstring& plugin_path() { return plugin_path_; } + + private: + friend class PluginProcessFactory; + PluginProcess(const std::wstring& channel_name, + const std::wstring& plugin_path); + virtual ~PluginProcess(); + + virtual void OnFinalRelease(); + void Shutdown(); + void OnProcessShutdownTimeout(); + + const std::wstring plugin_path_; + + // The thread where plugin instances live. Since NPAPI plugins weren't + // created with multi-threading in mind, running multiple instances on + // different threads would be asking for trouble. + PluginThread plugin_thread_; + + DISALLOW_EVIL_CONSTRUCTORS(PluginProcess); +}; + +#endif // CHROME_PLUGIN_PLUGIN_PROCESS_H__ diff --git a/chrome/plugin/plugin_thread.cc b/chrome/plugin/plugin_thread.cc new file mode 100644 index 0000000..3c117df --- /dev/null +++ b/chrome/plugin/plugin_thread.cc @@ -0,0 +1,206 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include <windows.h> +#include <objbase.h> + +#include "chrome/plugin/plugin_thread.h" + +#include "chrome/common/chrome_plugin_lib.h" +#include "chrome/common/ipc_logging.h" +#include "chrome/common/notification_service.h" +#include "chrome/common/plugin_messages.h" +#include "chrome/plugin/chrome_plugin_host.h" +#include "chrome/plugin/npobject_util.h" +#include "chrome/plugin/plugin_process.h" +#include "webkit/glue/plugins/plugin_lib.h" +#include "webkit/glue/webkit_glue.h" + +PluginThread* PluginThread::plugin_thread_; + +PluginThread::PluginThread(PluginProcess* process, + const std::wstring& channel_name) + : plugin_process_(process), + channel_name_(channel_name), + owner_loop_(MessageLoop::current()), + preloaded_plugin_module_(NULL), + Thread("Chrome_PluginThread") { + DCHECK(plugin_process_); + DCHECK(owner_loop_); + DCHECK(!plugin_thread_); + plugin_thread_ = this; + + Start(); +} + +PluginThread::~PluginThread() { + Stop(); + plugin_thread_ = NULL; +} + +void PluginThread::OnChannelError() { + owner_loop_->Quit(); +} + +bool PluginThread::Send(IPC::Message* msg) { + return channel_->Send(msg); +} + +void PluginThread::OnMessageReceived(const IPC::Message& msg) { + if (msg.routing_id() == MSG_ROUTING_CONTROL) { + // Resource responses are sent to the resource dispatcher. + if (resource_dispatcher_->OnMessageReceived(msg)) + return; + IPC_BEGIN_MESSAGE_MAP(PluginThread, msg) + IPC_MESSAGE_HANDLER(PluginProcessMsg_CreateChannel, OnCreateChannel) + IPC_MESSAGE_HANDLER(PluginProcessMsg_ShutdownResponse, OnShutdownResponse) + IPC_MESSAGE_HANDLER(PluginProcessMsg_PluginMessage, OnPluginMessage) + IPC_MESSAGE_HANDLER(PluginProcessMsg_BrowserShutdown, OnBrowserShutdown) + IPC_END_MESSAGE_MAP() + } else { + NOTREACHED() << "Only control messages should reach PluginThread."; + } +} + +void PluginThread::Init() { + PatchNPNFunctions(); + CoInitialize(NULL); + channel_.reset(new IPC::SyncChannel(channel_name_, + IPC::Channel::MODE_CLIENT, this, owner_loop_, true)); + notification_service_.reset(new NotificationService); + resource_dispatcher_ = new ResourceDispatcher(this); + + // Preload the dll to avoid loading, unloading then reloading + preloaded_plugin_module_ = NPAPI::PluginLib::LoadPluginHelper( + plugin_process_->plugin_path().c_str()); + + ChromePluginLib::Create(plugin_process_->plugin_path(), + GetCPBrowserFuncsForPlugin()); + + scoped_refptr<NPAPI::PluginLib> plugin = + NPAPI::PluginLib::CreatePluginLib(plugin_process_->plugin_path()); + if (plugin.get()) { + plugin->NP_Initialize(); + } + + // 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); + +#ifdef IPC_MESSAGE_LOG_ENABLED + IPC::Logging::current()->SetIPCSender(this); +#endif +} + +void PluginThread::CleanUp() { +#ifdef IPC_MESSAGE_LOG_ENABLED + IPC::Logging::current()->SetIPCSender(NULL); +#endif + if (preloaded_plugin_module_) { + FreeLibrary(preloaded_plugin_module_); + preloaded_plugin_module_ = NULL; + } + PluginChannelBase::CleanupChannels(); + NPAPI::PluginLib::UnloadAllPlugins(); + ChromePluginLib::UnloadAllPlugins(); + notification_service_.reset(); + resource_dispatcher_ = NULL; + CoUninitialize(); +} + +void PluginThread::OnCreateChannel(int process_id, HANDLE renderer_handle) { + std::wstring channel_name; + scoped_refptr<PluginChannel> channel = + PluginChannel::GetPluginChannel(process_id, renderer_handle, owner_loop_); + if (channel.get()) + channel_name = channel->channel_name(); + + Send(new PluginProcessHostMsg_ChannelCreated(process_id, channel_name)); +} + +void PluginThread::OnShutdownResponse(bool ok_to_shutdown) { + PluginProcess::ShutdownProcessResponse(ok_to_shutdown); +} + +void PluginThread::OnBrowserShutdown() { + PluginProcess::BrowserShutdown(); +} + +void PluginThread::OnPluginMessage(const std::vector<unsigned char> &data) { + // We Add/Release ref here to ensure that something will trigger the + // shutdown mechanism for processes started in the absence of renderer's + // opening a plugin channel. + PluginProcess::AddRefProcess(); + ChromePluginLib *chrome_plugin = + ChromePluginLib::Find(plugin_process_->plugin_path()); + if (chrome_plugin) { + void *data_ptr = const_cast<void*>(reinterpret_cast<const void*>(&data[0])); + uint32 data_len = static_cast<uint32>(data.size()); + chrome_plugin->functions().on_message(data_ptr, data_len); + } + PluginProcess::ReleaseProcess(); +} + +namespace webkit_glue { + +bool DownloadUrl(const std::string& url, HWND caller_window) { + PluginThread* plugin_thread = PluginThread::GetPluginThread(); + if (!plugin_thread) { + return false; + } + + IPC::Message* message = + new PluginProcessHostMsg_DownloadUrl(MSG_ROUTING_NONE, url, + ::GetCurrentProcessId(), + caller_window); + return plugin_thread->Send(message); +} + +bool GetPluginFinderURL(std::string* plugin_finder_url) { + if (!plugin_finder_url) { + NOTREACHED(); + return false; + } + + PluginThread* plugin_thread = PluginThread::GetPluginThread(); + if (!plugin_thread) { + return false; + } + + plugin_thread->Send( + new PluginProcessHostMsg_GetPluginFinderUrl(plugin_finder_url)); + DCHECK(!plugin_finder_url->empty()); + return true; +} + +bool IsDefaultPluginEnabled() { + return true; +} + +} // namespace webkit_glue diff --git a/chrome/plugin/plugin_thread.h b/chrome/plugin/plugin_thread.h new file mode 100644 index 0000000..5a9fd37 --- /dev/null +++ b/chrome/plugin/plugin_thread.h @@ -0,0 +1,99 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef CHROME_PLUGIN_PLUGIN_THREAD_H__ +#define CHROME_PLUGIN_PLUGIN_THREAD_H__ + +#include "base/thread.h" +#include "chrome/common/ipc_sync_channel.h" +#include "chrome/common/message_router.h" +#include "chrome/common/resource_dispatcher.h" +#include "chrome/plugin/plugin_channel.h" + +class PluginProcess; +class NotificationService; + +// 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 IPC::Channel::Listener, + public IPC::Message::Sender, + public Thread { + public: + PluginThread(PluginProcess *process, const std::wstring& channel_name); + ~PluginThread(); + + // IPC::Channel::Listener implementation: + virtual void OnMessageReceived(const IPC::Message& msg); + virtual void OnChannelError(); + + // IPC::Message::Sender implementation: + virtual bool Send(IPC::Message* msg); + + // Returns the one plugin thread. + static PluginThread* GetPluginThread() { return plugin_thread_; } + + // Returns the one true dispatcher. + ResourceDispatcher* resource_dispatcher() { return resource_dispatcher_.get(); } + + private: + // Thread implementation: + void Init(); + void CleanUp(); + + void OnCreateChannel(int process_id, HANDLE renderer_handle); + void OnShutdownResponse(bool ok_to_shutdown); + void OnPluginMessage(const std::vector<uint8> &data); + void OnBrowserShutdown(); + + // The process that has created this thread + PluginProcess *plugin_process_; + + // The message loop used to run tasks on the thread that started this thread. + MessageLoop* owner_loop_; + + std::wstring channel_name_; + scoped_ptr<IPC::SyncChannel> channel_; + + scoped_ptr<NotificationService> notification_service_; + + // Handles resource loads for this view. + // NOTE: this object lives on the owner thread. + scoped_refptr<ResourceDispatcher> resource_dispatcher_; + + // The plugin module which is preloaded in Init + HMODULE preloaded_plugin_module_; + + // Points to the one PluginThread object in the process. + static PluginThread* plugin_thread_; + + DISALLOW_EVIL_CONSTRUCTORS(PluginThread); +}; + +#endif // CHROME_PLUGIN_PLUGIN_THREAD_H__ diff --git a/chrome/plugin/webplugin_delegate_stub.cc b/chrome/plugin/webplugin_delegate_stub.cc new file mode 100644 index 0000000..0678b14 --- /dev/null +++ b/chrome/plugin/webplugin_delegate_stub.cc @@ -0,0 +1,462 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "chrome/plugin/webplugin_delegate_stub.h" + +#include "base/command_line.h" +#include "base/time.h" +#include "base/gfx/bitmap_header.h" +#include "base/gfx/platform_device.h" +#include "bindings/npapi.h" +#include "bindings/npruntime.h" +#include "chrome/common/chrome_switches.h" +#include "chrome/common/gfx/emf.h" +#include "chrome/common/plugin_messages.h" +#include "chrome/common/win_util.h" +#include "chrome/plugin/npobject_stub.h" +#include "chrome/plugin/plugin_channel.h" +#include "chrome/plugin/plugin_thread.h" +#include "chrome/plugin/webplugin_proxy.h" +#include "webkit/glue/plugins/webplugin_delegate_impl.h" +#include "webkit/glue/webcursor.h" + +class FinishDestructionTask : public Task { + public: + FinishDestructionTask(WebPluginDelegate* delegate, WebPlugin* webplugin) + : delegate_(delegate), webplugin_(webplugin) { } + + void Run() { + // WebPlugin must outlive WebPluginDelegate. + if (delegate_) + delegate_->PluginDestroyed(); + + delete webplugin_; + } + + private: + WebPluginDelegate* delegate_; + 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) { + DCHECK(channel); +} + +WebPluginDelegateStub::~WebPluginDelegateStub() { + if (channel_->in_send()) { + // The delegate or an npobject is in the callstack, so don't delete it + // right away. + MessageLoop::current()->PostTask(FROM_HERE, + new FinishDestructionTask(delegate_, webplugin_)); + } else { + // Safe to delete right away. + if (delegate_) + delegate_->PluginDestroyed(); + + delete webplugin_; + } +} + +void WebPluginDelegateStub::OnMessageReceived(const IPC::Message& msg) { + // 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. + AddRef(); + + 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_HandleEvent, OnHandleEvent) + IPC_MESSAGE_HANDLER(PluginMsg_Paint, OnPaint) + IPC_MESSAGE_HANDLER(PluginMsg_Print, OnPrint) + IPC_MESSAGE_HANDLER(PluginMsg_PaintIntoSharedMemory, + OnPaintIntoSharedMemory) + IPC_MESSAGE_HANDLER(PluginMsg_GetPluginScriptableObject, + OnGetPluginScriptableObject) + IPC_MESSAGE_HANDLER(PluginMsg_UpdateGeometry, OnUpdateGeometry) + IPC_MESSAGE_HANDLER(PluginMsg_SendJavaScriptStream, + OnSendJavaScriptStream) + 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_URLRequestRouted, OnURLRequestRouted) + IPC_MESSAGE_UNHANDLED_ERROR() + IPC_END_MESSAGE_MAP() + + Release(); +} + +bool WebPluginDelegateStub::Send(IPC::Message* msg) { + return channel_->Send(msg); +} + +void WebPluginDelegateStub::OnInit(const PluginMsg_Init_Params& params, + bool* result) { + *result = false; + int argc = static_cast<int>(params.arg_names.size()); + if (argc != static_cast<int>(params.arg_values.size())) { + NOTREACHED(); + return; + } + + char **argn = new char*[argc]; + char **argv = new char*[argc]; + for (int i = 0; i < argc; ++i) { + argn[i] = const_cast<char*>(params.arg_names[i].c_str()); + argv[i] = const_cast<char*>(params.arg_values[i].c_str()); + } + + CommandLine command_line; + std::wstring path = command_line.GetSwitchValue(switches::kPluginPath); + delegate_ = WebPluginDelegateImpl::Create( + path, mime_type_, params.containing_window); + if (delegate_) { + webplugin_ = new WebPluginProxy( + channel_, instance_id_, delegate_, params.modal_dialog_event); + *result = delegate_->Initialize( + params.url, argn, argv, argc, webplugin_, params.load_manually); + } + + delete[] argn; + delete[] argv; +} + +void WebPluginDelegateStub::OnWillSendRequest(int id, const GURL& url) { + WebPluginResourceClient* client = webplugin_->GetResourceClient(id); + if (!client) + return; + + client->WillSendRequest(url); +} + +void WebPluginDelegateStub::OnDidReceiveResponse( + const PluginMsg_DidReceiveResponseParams& params, bool* cancel) { + *cancel = false; + WebPluginResourceClient* client = webplugin_->GetResourceClient(params.id); + if (!client) + return; + + client->DidReceiveResponse(params.mime_type, + params.headers, + params.expected_length, + params.last_modified, + cancel); +} + +void WebPluginDelegateStub::OnDidReceiveData(int id, + const std::vector<char>& buffer) { + WebPluginResourceClient* client = webplugin_->GetResourceClient(id); + if (!client) + return; + + client->DidReceiveData(&buffer.front(), static_cast<int>(buffer.size())); +} + +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(int reason) { + delegate_->DidFinishLoadWithReason(reason); +} + +void WebPluginDelegateStub::OnSetFocus() { + delegate_->SetFocus(); +} + +void WebPluginDelegateStub::OnHandleEvent(const NPEvent& event, + bool* handled, + WebCursor* cursor) { + *handled = delegate_->HandleEvent(const_cast<NPEvent*>(&event), cursor); +} + +void WebPluginDelegateStub::OnPaint(const PluginMsg_Paint_Params& params) { + // Convert the shared memory handle to a handle that works in our process, + // and then use that to create an HDC. + win_util::ScopedHandle shared_section(win_util::GetSectionFromProcess( + params.shared_memory, channel_->renderer_handle(), false)); + + if (shared_section == NULL) { + NOTREACHED(); + return; + } + + void* data = NULL; + HDC screen_dc = GetDC(NULL); + BITMAPINFOHEADER bitmap_header; + gfx::CreateBitmapHeader(params.size.width(), params.size.height(), + &bitmap_header); + win_util::ScopedBitmap hbitmap(CreateDIBSection( + screen_dc, reinterpret_cast<const BITMAPINFO*>(&bitmap_header), + DIB_RGB_COLORS, &data, + shared_section, 0)); + ReleaseDC(NULL, screen_dc); + if (hbitmap == NULL) { + NOTREACHED(); + return; + } + + win_util::ScopedHDC hdc(CreateCompatibleDC(NULL)); + if (hdc == NULL) { + NOTREACHED(); + return; + } + gfx::PlatformDevice::InitializeDC(hdc); + SelectObject(hdc, hbitmap); + SetWorldTransform(hdc, ¶ms.xf); + + win_util::ScopedHRGN hrgn(CreateRectRgnIndirect(¶ms.clip_rect.ToRECT())); + SelectClipRgn(hdc, hrgn); + webplugin_->WillPaint(); + delegate_->Paint(hdc, params.damaged_rect); +} + +void WebPluginDelegateStub::OnPrint(PluginMsg_PrintResponse_Params* params) { + gfx::Emf emf; + if (!emf.CreateDc(NULL, NULL)) { + NOTREACHED(); + return; + } + HDC hdc = emf.hdc(); + gfx::PlatformDevice::InitializeDC(hdc); + delegate_->Print(hdc); + if (!emf.CloseDc()) { + NOTREACHED(); + return; + } + + size_t size = emf.GetDataSize(); + DCHECK(size); + params->size = size; + SharedMemory shared_buf; + CreateSharedBuffer(size, &shared_buf, ¶ms->shared_memory); + + // Retrieve a copy of the data. + bool success = emf.GetData(shared_buf.memory(), size); + DCHECK(success); +} + +void WebPluginDelegateStub::OnPaintIntoSharedMemory( + const PluginMsg_Paint_Params& params, + SharedMemoryHandle* emf_buffer, + size_t* bytes) { + *emf_buffer = NULL; + *bytes = 0; + + gfx::Emf emf; + if (!emf.CreateDc(NULL, NULL)) { + NOTREACHED(); + return; + } + HDC hdc = emf.hdc(); + gfx::PlatformDevice::InitializeDC(hdc); + + if (delegate_->windowless()) { + WindowlessPaint(hdc, params); + } else { + WindowedPaint(hdc, params.damaged_rect); + } + + // Need to send back the data as shared memory. + if (!emf.CloseDc()) { + NOTREACHED(); + return; + } + + size_t size = emf.GetDataSize(); + DCHECK(size); + *bytes = size; + SharedMemory shared_buf; + CreateSharedBuffer(size, &shared_buf, emf_buffer); + + // Retrieve a copy of the data. + bool success = emf.GetData(shared_buf.memory(), size); + DCHECK(success); +} + +void WebPluginDelegateStub::WindowedPaint(HDC hdc, + const gfx::Rect& window_rect) { + // Use the NPAPI print() function to render the plugin. + delegate_->Print(hdc); +} + +void WebPluginDelegateStub::WindowlessPaint( + HDC hdc, + const PluginMsg_Paint_Params& params) { + void* data = NULL; + HDC screen_dc = GetDC(NULL); + BITMAPINFOHEADER bitmap_header; + gfx::CreateBitmapHeader(params.size.width(), params.size.height(), + &bitmap_header); + win_util::ScopedBitmap hbitmap(CreateDIBSection( + screen_dc, reinterpret_cast<const BITMAPINFO*>(&bitmap_header), + DIB_RGB_COLORS, &data, NULL, 0)); + ReleaseDC(NULL, screen_dc); + if (hbitmap == NULL) { + NOTREACHED(); + return; + } + SelectObject(hdc, hbitmap); + + // Apply transform and clipping. + SetWorldTransform(hdc, ¶ms.xf); + win_util::ScopedHRGN hrgn(CreateRectRgnIndirect(¶ms.clip_rect.ToRECT())); + SelectClipRgn(hdc, hrgn); + webplugin_->WillPaint(); + delegate_->Paint(hdc, params.damaged_rect); +} + +void WebPluginDelegateStub::OnUpdateGeometry(const gfx::Rect& window_rect, + const gfx::Rect& clip_rect, + bool visible) { + delegate_->UpdateGeometry(window_rect, clip_rect, visible); +} + +void WebPluginDelegateStub::OnGetPluginScriptableObject(int* route_id, + void** npobject_ptr) { + NPObject* object = delegate_->GetPluginScriptableObject(); + if (!object) { + *route_id = MSG_ROUTING_NONE; + return; + } + + *route_id = channel_->GenerateRouteID(); + *npobject_ptr = object; + // The stub will delete itself when the proxy tells it that it's released, or + // otherwise when the channel is closed. + NPObjectStub* stub = new NPObjectStub(object, channel_.get(), *route_id); + + // Release ref added by GetPluginScriptableObject (our stub holds its own). + NPN_ReleaseObject(object); +} + +void WebPluginDelegateStub::OnSendJavaScriptStream(const std::string& url, + const std::wstring& result, + bool success, + bool notify_needed, + int notify_data) { + delegate_->SendJavaScriptStream(url, result, success, notify_needed, + notify_data); +} + +void WebPluginDelegateStub::OnDidReceiveManualResponse( + const std::string& 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( + size_t size, + SharedMemory* shared_buf, + SharedMemoryHandle* remote_handle) { + if (!shared_buf->Create(std::wstring(), false, false, size)) { + NOTREACHED(); + return; + } + if (!shared_buf->Map(size)) { + NOTREACHED(); + shared_buf->Close(); + return; + } + + 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". +} + +void WebPluginDelegateStub::OnHandleURLRequestReply( + const PluginMsg_URLRequestReply_Params& params) { + WebPluginResourceClient* resource_client = + delegate_->CreateResourceClient(params.resource_id, params.url, + params.notify_needed, + params.notify_data); + webplugin_->OnResourceCreated(params.resource_id, resource_client); +} + +void WebPluginDelegateStub::OnURLRequestRouted(const std::string& url, + bool notify_needed, + HANDLE notify_data) { + delegate_->URLRequestRouted(url, notify_needed, notify_data); +} diff --git a/chrome/plugin/webplugin_delegate_stub.h b/chrome/plugin/webplugin_delegate_stub.h new file mode 100644 index 0000000..27ecac6 --- /dev/null +++ b/chrome/plugin/webplugin_delegate_stub.h @@ -0,0 +1,137 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef CHROME_PLUGIN_WEBPLUGIN_DELEGATE_STUB_H__ +#define CHROME_PLUGIN_WEBPLUGIN_DELEGATE_STUB_H__ + +#include <queue> + +#include "base/gfx/rect.h" +#include "base/ref_counted.h" +#include "base/task.h" +#include "bindings/npapi.h" +#include "chrome/common/ipc_channel.h" +#include "chrome/common/plugin_messages.h" + +class GURL; +class PluginChannel; +class WebPluginProxy; +class WebPluginDelegateImpl; +struct PluginMsg_Init_Params; +struct PluginMsg_Paint_Params; +struct PluginMsg_DidReceiveResponseParams; +class WebCursor; + +// Converts the IPC messages from WebPluginDelegateProxy into calls to the +// actual WebPluginDelegate 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); + ~WebPluginDelegateStub(); + + // IPC::Channel::Listener implementation: + virtual void OnMessageReceived(const IPC::Message& msg); + + // IPC::Message::Sender implementation: + virtual bool Send(IPC::Message* msg); + + int instance_id() { return instance_id_; } + + private: + // 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); + void OnDidReceiveResponse(const PluginMsg_DidReceiveResponseParams& params, + bool* cancel); + void OnDidReceiveData(int id, const std::vector<char>& buffer); + void OnDidFinishLoading(int id); + void OnDidFail(int id); + + void OnDidFinishLoadWithReason(int reason); + void OnSetFocus(); + void OnHandleEvent(const NPEvent& event, bool* handled, + WebCursor* cursor); + + void OnPaint(const PluginMsg_Paint_Params& params); + + void OnPrint(PluginMsg_PrintResponse_Params* params); + + // Paints the plugin into a buffer. It roughly does the same as OnPaint (i.e. + // painting a plugin) except that the plugin window is always renderered into + // an EMF buffer and that it is effective for windowed plugins too. + void OnPaintIntoSharedMemory(const PluginMsg_Paint_Params& params, + SharedMemoryHandle* emf_buffer, size_t* bytes); + // Paints a windowed plugin into a device context. + void WindowedPaint(HDC hdc, const gfx::Rect& window_rect); + // Paints a windowless plugin into a device context. + void WindowlessPaint(HDC hdc, + const PluginMsg_Paint_Params& params); + void OnUpdateGeometry(const gfx::Rect& window_rect, + const gfx::Rect& clip_rect, bool visible); + void OnGetPluginScriptableObject(int* route_id, void** npobject_ptr); + void OnSendJavaScriptStream(const std::string& url, + const std::wstring& result, + bool success, bool notify_needed, + int notify_data); + + void OnDidReceiveManualResponse( + const std::string& url, + const PluginMsg_DidReceiveResponseParams& params); + void OnDidReceiveManualData(const std::vector<char>& buffer); + void OnDidFinishManualLoading(); + void OnDidManualLoadFail(); + void OnInstallMissingPlugin(); + + void OnHandleURLRequestReply( + const PluginMsg_URLRequestReply_Params& params); + + void OnURLRequestRouted(const std::string& url, bool notify_needed, + HANDLE notify_data); + + void CreateSharedBuffer(size_t size, + SharedMemory* shared_buf, + SharedMemoryHandle* remote_handle); + + int instance_id_; + std::string mime_type_; + + scoped_refptr<PluginChannel> channel_; + + WebPluginDelegateImpl* delegate_; + WebPluginProxy* webplugin_; + + DISALLOW_EVIL_CONSTRUCTORS(WebPluginDelegateStub); +}; + +#endif // CHROME_PLUGIN_WEBPLUGIN_DELEGATE_STUB_H__ diff --git a/chrome/plugin/webplugin_proxy.cc b/chrome/plugin/webplugin_proxy.cc new file mode 100644 index 0000000..b969e58 --- /dev/null +++ b/chrome/plugin/webplugin_proxy.cc @@ -0,0 +1,269 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "chrome/plugin/webplugin_proxy.h" + +#include "base/scoped_handle.h" +#include "base/singleton.h" +#include "chrome/common/plugin_messages.h" +#include "chrome/plugin/plugin_channel.h" +#include "chrome/plugin/webplugin_delegate_stub.h" +#include "chrome/plugin/npobject_proxy.h" +#include "chrome/plugin/npobject_util.h" +#include "webkit/glue/webplugin_delegate.h" + +typedef std::map<CPBrowsingContext, WebPluginProxy*> ContextMap; +static ContextMap& GetContextMap() { + return *Singleton<ContextMap>::get(); +} + +WebPluginProxy::WebPluginProxy( + PluginChannel* channel, + int route_id, + WebPluginDelegate* delegate, + HANDLE modal_dialog_event) + : channel_(channel), + route_id_(route_id), + cp_browsing_context_(0), + window_npobject_(NULL), + plugin_element_(NULL), + delegate_(delegate), + waiting_for_paint_(false) { + + HANDLE event; + BOOL result = DuplicateHandle(channel->renderer_handle(), + modal_dialog_event, + GetCurrentProcess(), + &event, + SYNCHRONIZE, + FALSE, + 0); + DCHECK(result) << "Couldn't duplicate the modal dialog handle for the plugin."; + modal_dialog_event_.Set(event); +} + +WebPluginProxy::~WebPluginProxy() { + if (cp_browsing_context_) + GetContextMap().erase(cp_browsing_context_); +} + +bool WebPluginProxy::Send(IPC::Message* msg) { + return channel_->Send(msg); +} + +void WebPluginProxy::SetWindow(HWND window, HANDLE pump_messages_event) { + HANDLE pump_messages_event_for_renderer = NULL; + + if (pump_messages_event) { + DCHECK(window == 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_SetWindow(route_id_, window, + pump_messages_event_for_renderer)); +} + +void WebPluginProxy::CancelResource(int id) { + Send(new PluginHostMsg_CancelResource(route_id_, id)); + resource_clients_.erase(id); +} + +void WebPluginProxy::Invalidate() { + Send(new PluginHostMsg_Invalidate(route_id_)); +} + +void WebPluginProxy::InvalidateRect(const gfx::Rect& rect) { + // Ignore NPN_InvalidateRect calls with empty rects. + if (rect.IsEmpty()) { + return; + } + // Only send a single InvalidateRect message at a time. From WillPaint we + // will dispatch an additional InvalidateRect message if necessary. + if (waiting_for_paint_) { + damaged_rect_ = damaged_rect_.Union(rect); + } else { + waiting_for_paint_ = true; + Send(new PluginHostMsg_InvalidateRect(route_id_, rect)); + } +} + +NPObject* WebPluginProxy::GetWindowScriptNPObject() { + if (window_npobject_) + return NPN_RetainObject(window_npobject_); + + int npobject_route_id = channel_->GenerateRouteID(); + bool success = false; + void* npobject_ptr; + Send(new PluginHostMsg_GetWindowScriptNPObject( + route_id_, npobject_route_id, &success, &npobject_ptr)); + if (!success) + return NULL; + + window_npobject_ = NPObjectProxy::Create(channel_, + npobject_route_id, + npobject_ptr, + modal_dialog_event_.Get()); + + return window_npobject_; +} + +NPObject* WebPluginProxy::GetPluginElement() { + if (plugin_element_) + return NPN_RetainObject(plugin_element_); + + int npobject_route_id = channel_->GenerateRouteID(); + bool success = false; + void* npobject_ptr; + Send(new PluginHostMsg_GetPluginElement( + route_id_, npobject_route_id, &success, &npobject_ptr)); + if (!success) + return NULL; + + plugin_element_ = NPObjectProxy::Create(channel_, + npobject_route_id, + npobject_ptr, + modal_dialog_event_.Get()); + + return plugin_element_; +} + +void WebPluginProxy::SetCookie(const GURL& url, + const GURL& policy_url, + const std::string& cookie) { + Send(new PluginHostMsg_SetCookie(route_id_, url, policy_url, cookie)); +} + +std::string WebPluginProxy::GetCookies(const GURL& url, + const GURL& policy_url) { + std::string cookies; + Send(new PluginHostMsg_GetCookies(route_id_, url, policy_url, &cookies)); + + return cookies; +} + +void WebPluginProxy::ShowModalHTMLDialog(const GURL& url, int width, int height, + const std::string& json_arguments, + std::string* json_retval) { + PluginHostMsg_ShowModalHTMLDialog* msg = + new PluginHostMsg_ShowModalHTMLDialog( + route_id_, url, width, height, json_arguments, json_retval); + + // Create a new event and set it. This forces us to pump messages while + // waiting for a response (which won't come until the dialog is closed). This + // avoids a deadlock. + ScopedHandle event(CreateEvent(NULL, FALSE, TRUE, NULL)); + msg->set_pump_messages_event(event); + + Send(msg); +} + +void WebPluginProxy::OnMissingPluginStatus(int status) { + Send(new PluginHostMsg_MissingPluginStatus(route_id_, status)); +} + +CPBrowsingContext WebPluginProxy::GetCPBrowsingContext() { + if (cp_browsing_context_ == 0) { + Send(new PluginHostMsg_GetCPBrowsingContext(route_id_, + &cp_browsing_context_)); + GetContextMap()[cp_browsing_context_] = this; + } + return cp_browsing_context_; +} + +WebPluginProxy* WebPluginProxy::FromCPBrowsingContext( + CPBrowsingContext context) { + return GetContextMap()[context]; +} + +WebPluginResourceClient* WebPluginProxy::GetResourceClient(int id) { + ResourceClientMap::iterator iterator = resource_clients_.find(id); + if (iterator == resource_clients_.end()) { + NOTREACHED(); + return NULL; + } + + return iterator->second; +} + +void WebPluginProxy::WillPaint() { + // 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_); + damaged_rect_ = gfx::Rect(); + } +} + +void WebPluginProxy::OnResourceCreated(int resource_id, HANDLE cookie) { + WebPluginResourceClient* resource_client = + reinterpret_cast<WebPluginResourceClient*>(cookie); + if (!resource_client) { + NOTREACHED(); + return; + } + + DCHECK(resource_clients_.find(resource_id) == resource_clients_.end()); + resource_clients_[resource_id] = resource_client; +} + +void WebPluginProxy::HandleURLRequest(const char *method, + bool is_javascript_url, + const char* target, unsigned int len, + const char* buf, bool is_file_data, + bool notify, const char* url, + void* notify_data, bool popups_allowed) { + if (!url) { + NOTREACHED(); + return; + } + + PluginHostMsg_URLRequest_Params params; + params.method = method; + params.is_javascript_url = is_javascript_url; + if (target) + params.target = std::string(target); + + if (len) { + params.buffer.resize(len); + memcpy(¶ms.buffer.front(), buf, len); + } + + params.is_file_data = is_file_data; + params.notify = notify; + params.url = url; + params.notify_data = notify_data; + params.popups_allowed = popups_allowed; + + Send(new PluginHostMsg_URLRequest(route_id_, params)); +} diff --git a/chrome/plugin/webplugin_proxy.h b/chrome/plugin/webplugin_proxy.h new file mode 100644 index 0000000..6c862e1 --- /dev/null +++ b/chrome/plugin/webplugin_proxy.h @@ -0,0 +1,116 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef CHROME_PLUGIN_PLUGIN_WEBPLUGIN_PROXY_H__ +#define CHROME_PLUGIN_PLUGIN_WEBPLUGIN_PROXY_H__ + +#include <hash_map> + +#include "base/ref_counted.h" +#include "base/scoped_handle.h" +#include "chrome/common/ipc_message.h" +#include "chrome/common/chrome_plugin_api.h" +#include "webkit/glue/webplugin.h" + +class PluginChannel; +class WebPluginDelegate; + +// This is an implementation of WebPlugin that proxies all calls to the +// renderer. +class WebPluginProxy : public WebPlugin { + public: + // Creates a new proxy for WebPlugin, using the given sender to send the + // marshalled WebPlugin calls. + WebPluginProxy(PluginChannel* channel, + int route_id, + WebPluginDelegate* delegate, + HANDLE modal_dialog_event); + ~WebPluginProxy(); + + // WebPlugin overrides + void SetWindow(HWND window, HANDLE pump_messages_event); + void CancelResource(int id); + void Invalidate(); + void InvalidateRect(const gfx::Rect& rect); + NPObject* GetWindowScriptNPObject(); + NPObject* GetPluginElement(); + void SetCookie(const GURL& url, + const GURL& policy_url, + const std::string& cookie); + std::string GetCookies(const GURL& url, const GURL& policy_url); + + void ShowModalHTMLDialog(const GURL& url, int width, int height, + const std::string& json_arguments, + std::string* json_retval); + void OnMissingPluginStatus(int status); + // class-specific methods + + // Retrieves the browsing context associated with the renderer this plugin + // is in. Calling multiple times will return the same value. + CPBrowsingContext GetCPBrowsingContext(); + + // Retrieves the WebPluginProxy for the given context that was returned by + // GetCPBrowsingContext, or NULL if not found. + static WebPluginProxy* FromCPBrowsingContext(CPBrowsingContext context); + + // Returns a WebPluginResourceClient object given its id, or NULL if no + // object with that id exists. + WebPluginResourceClient* GetResourceClient(int id); + + void WillPaint(); + + // Notification received on a plugin issued resource request + // creation. + void OnResourceCreated(int resource_id, HANDLE cookie); + + void HandleURLRequest(const char *method, + bool is_javascript_url, + const char* target, unsigned int len, + const char* buf, bool is_file_data, + bool notify, const char* url, + void* notify_data, bool popups_allowed); + + private: + bool Send(IPC::Message* msg); + + typedef stdext::hash_map<int, WebPluginResourceClient*> ResourceClientMap; + ResourceClientMap resource_clients_; + + scoped_refptr<PluginChannel> channel_; + int route_id_; + NPObject* window_npobject_; + NPObject* plugin_element_; + WebPluginDelegate* delegate_; + gfx::Rect damaged_rect_; + bool waiting_for_paint_; + uint32 cp_browsing_context_; + ScopedHandle modal_dialog_event_; +}; + +#endif // CHROME_PLUGIN_PLUGIN_WEBPLUGIN_PROXY_H__ |