diff options
author | brettw@chromium.org <brettw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-02-13 17:50:20 +0000 |
---|---|---|
committer | brettw@chromium.org <brettw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-02-13 17:50:20 +0000 |
commit | a08ebeaea39acfe007d9ee9b25b28efd04011e5a (patch) | |
tree | c878b32554af650294155ab21491dc086cff0939 /chrome | |
parent | 9d7557f78d893c56afcf65f2e6317feabb9af796 (diff) | |
download | chromium_src-a08ebeaea39acfe007d9ee9b25b28efd04011e5a.zip chromium_src-a08ebeaea39acfe007d9ee9b25b28efd04011e5a.tar.gz chromium_src-a08ebeaea39acfe007d9ee9b25b28efd04011e5a.tar.bz2 |
Share PPAPI out-of-process plugins between renderer processes.
This provides the hook-up for plugin sharing but not shutdown or cleanup from
errors. There is still a lot of work to do cleaning up in the plugin and the
browser when a renderer dies, or cleaning up in the renderer and browser when a
plugin dies. Currently, even the normal exit case crashes in the browser. But
fixing it in this patch would be too complicated to write or review, so I'm
going to do shutdown & error handling in a followup.
Review URL: http://codereview.chromium.org/6486034
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@74766 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
-rw-r--r-- | chrome/browser/chrome_plugin_host.cc | 4 | ||||
-rw-r--r-- | chrome/browser/plugin_data_remover.cc | 2 | ||||
-rw-r--r-- | chrome/browser/plugin_service.cc | 94 | ||||
-rw-r--r-- | chrome/browser/plugin_service.h | 30 | ||||
-rw-r--r-- | chrome/browser/plugin_service_browsertest.cc | 12 | ||||
-rw-r--r-- | chrome/browser/ppapi_plugin_process_host.cc | 137 | ||||
-rw-r--r-- | chrome/browser/ppapi_plugin_process_host.h | 54 | ||||
-rw-r--r-- | chrome/browser/renderer_host/render_message_filter.cc | 102 | ||||
-rw-r--r-- | chrome/browser/renderer_host/render_message_filter.h | 4 | ||||
-rw-r--r-- | chrome/ppapi_plugin/ppapi_thread.cc | 101 | ||||
-rw-r--r-- | chrome/ppapi_plugin/ppapi_thread.h | 36 | ||||
-rw-r--r-- | chrome/renderer/pepper_plugin_delegate_impl.cc | 6 |
12 files changed, 370 insertions, 212 deletions
diff --git a/chrome/browser/chrome_plugin_host.cc b/chrome/browser/chrome_plugin_host.cc index ad4462e..2b020d4 100644 --- a/chrome/browser/chrome_plugin_host.cc +++ b/chrome/browser/chrome_plugin_host.cc @@ -702,7 +702,7 @@ CPBool STDCALL CPB_IsPluginProcessRunning(CPID id) { PluginService* service = PluginService::GetInstance(); if (!service) return false; - PluginProcessHost *host = service->FindPluginProcess(plugin->filename()); + PluginProcessHost *host = service->FindNpapiPluginProcess(plugin->filename()); return host ? true : false; } @@ -720,7 +720,7 @@ CPError STDCALL CPB_SendMessage(CPID id, const void *data, uint32 data_len) { if (!service) return CPERR_FAILURE; PluginProcessHost *host = - service->FindOrStartPluginProcess(plugin->filename()); + service->FindOrStartNpapiPluginProcess(plugin->filename()); if (!host) return CPERR_FAILURE; diff --git a/chrome/browser/plugin_data_remover.cc b/chrome/browser/plugin_data_remover.cc index 188140a..a32e12b 100644 --- a/chrome/browser/plugin_data_remover.cc +++ b/chrome/browser/plugin_data_remover.cc @@ -49,7 +49,7 @@ base::WaitableEvent* PluginDataRemover::StartRemoving( is_removing_ = true; AddRef(); - PluginService::GetInstance()->OpenChannelToPlugin( + PluginService::GetInstance()->OpenChannelToNpapiPlugin( 0, 0, GURL(), mime_type_, this); BrowserThread::PostDelayedTask( diff --git a/chrome/browser/plugin_service.cc b/chrome/browser/plugin_service.cc index 148f65e..a65bea6 100644 --- a/chrome/browser/plugin_service.cc +++ b/chrome/browser/plugin_service.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Copyright (c) 2011 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -18,6 +18,7 @@ #include "chrome/browser/chrome_plugin_host.h" #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/plugin_updater.h" +#include "chrome/browser/ppapi_plugin_process_host.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/renderer_host/render_process_host.h" #include "chrome/browser/renderer_host/render_view_host.h" @@ -240,7 +241,7 @@ const std::string& PluginService::GetUILocale() { return ui_locale_; } -PluginProcessHost* PluginService::FindPluginProcess( +PluginProcessHost* PluginService::FindNpapiPluginProcess( const FilePath& plugin_path) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); @@ -254,11 +255,27 @@ PluginProcessHost* PluginService::FindPluginProcess( return NULL; } -PluginProcessHost* PluginService::FindOrStartPluginProcess( +PpapiPluginProcessHost* PluginService::FindPpapiPluginProcess( const FilePath& plugin_path) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - PluginProcessHost* plugin_host = FindPluginProcess(plugin_path); + for (BrowserChildProcessHost::Iterator iter( + ChildProcessInfo::PPAPI_PLUGIN_PROCESS); + !iter.Done(); ++iter) { + PpapiPluginProcessHost* plugin = + static_cast<PpapiPluginProcessHost*>(*iter); + if (plugin->plugin_path() == plugin_path) + return plugin; + } + + return NULL; +} + +PluginProcessHost* PluginService::FindOrStartNpapiPluginProcess( + const FilePath& plugin_path) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + + PluginProcessHost* plugin_host = FindNpapiPluginProcess(plugin_path); if (plugin_host) return plugin_host; @@ -271,14 +288,42 @@ PluginProcessHost* PluginService::FindOrStartPluginProcess( // This plugin isn't loaded by any plugin process, so create a new process. scoped_ptr<PluginProcessHost> new_host(new PluginProcessHost()); if (!new_host->Init(info, ui_locale_)) { - NOTREACHED(); // Init is not expected to fail + NOTREACHED(); // Init is not expected to fail. return NULL; } + return new_host.release(); +} +PpapiPluginProcessHost* PluginService::FindOrStartPpapiPluginProcess( + const FilePath& plugin_path) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + + PpapiPluginProcessHost* plugin_host = FindPpapiPluginProcess(plugin_path); + if (plugin_host) + return plugin_host; + + // Validate that the plugin is actually registered. There should generally + // be very few plugins so a brute-force search is fine. + PepperPluginInfo* info = NULL; + for (size_t i = 0; i < ppapi_plugins_.size(); i++) { + if (ppapi_plugins_[i].path == plugin_path) { + info = &ppapi_plugins_[i]; + break; + } + } + if (!info) + return NULL; + + // This plugin isn't loaded by any plugin process, so create a new process. + scoped_ptr<PpapiPluginProcessHost> new_host(new PpapiPluginProcessHost); + if (!new_host->Init(plugin_path)) { + NOTREACHED(); // Init is not expected to fail. + return NULL; + } return new_host.release(); } -void PluginService::OpenChannelToPlugin( +void PluginService::OpenChannelToNpapiPlugin( int render_process_id, int render_view_id, const GURL& url, @@ -293,6 +338,16 @@ void PluginService::OpenChannelToPlugin( render_process_id, render_view_id, url, mime_type, client)); } +void PluginService::OpenChannelToPpapiPlugin( + const FilePath& path, + PpapiPluginProcessHost::Client* client) { + PpapiPluginProcessHost* plugin_host = FindOrStartPpapiPluginProcess(path); + if (plugin_host) + plugin_host->OpenChannelToPlugin(client); + else // Send error. + client->OnChannelOpened(base::kNullProcessHandle, IPC::ChannelHandle()); +} + void PluginService::GetAllowedPluginForOpenChannelToPlugin( int render_process_id, int render_view_id, @@ -320,7 +375,7 @@ void PluginService::FinishOpenChannelToPlugin( PluginProcessHost::Client* client) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - PluginProcessHost* plugin_host = FindOrStartPluginProcess(plugin_path); + PluginProcessHost* plugin_host = FindOrStartNpapiPluginProcess(plugin_path); if (plugin_host) plugin_host->OpenChannelToPlugin(client); else @@ -392,7 +447,7 @@ void PluginService::OnWaitableEventSignaled( static void ForceShutdownPlugin(const FilePath& plugin_path) { PluginProcessHost* plugin = - PluginService::GetInstance()->FindPluginProcess(plugin_path); + PluginService::GetInstance()->FindNpapiPluginProcess(plugin_path); if (plugin) plugin->ForceShutdown(); } @@ -490,26 +545,25 @@ void PluginService::OverridePluginForTab(OverriddenPlugin plugin) { } void PluginService::RegisterPepperPlugins() { - std::vector<PepperPluginInfo> plugins; - PepperPluginRegistry::ComputeList(&plugins); - for (size_t i = 0; i < plugins.size(); ++i) { + PepperPluginRegistry::ComputeList(&ppapi_plugins_); + for (size_t i = 0; i < ppapi_plugins_.size(); ++i) { webkit::npapi::WebPluginInfo info; - info.path = plugins[i].path; - info.name = plugins[i].name.empty() ? - plugins[i].path.BaseName().LossyDisplayName() : - ASCIIToUTF16(plugins[i].name); - info.desc = ASCIIToUTF16(plugins[i].description); + info.path = ppapi_plugins_[i].path; + info.name = ppapi_plugins_[i].name.empty() ? + ppapi_plugins_[i].path.BaseName().LossyDisplayName() : + ASCIIToUTF16(ppapi_plugins_[i].name); + info.desc = ASCIIToUTF16(ppapi_plugins_[i].description); info.enabled = webkit::npapi::WebPluginInfo::USER_ENABLED_POLICY_UNMANAGED; // TODO(evan): Pepper shouldn't require us to parse strings to get // the list of mime types out. if (!webkit::npapi::PluginList::ParseMimeTypes( - JoinString(plugins[i].mime_types, '|'), - plugins[i].file_extensions, - ASCIIToUTF16(plugins[i].type_descriptions), + JoinString(ppapi_plugins_[i].mime_types, '|'), + ppapi_plugins_[i].file_extensions, + ASCIIToUTF16(ppapi_plugins_[i].type_descriptions), &info.mime_types)) { LOG(ERROR) << "Error parsing mime types for " - << plugins[i].path.LossyDisplayName(); + << ppapi_plugins_[i].path.LossyDisplayName(); return; } diff --git a/chrome/browser/plugin_service.h b/chrome/browser/plugin_service.h index f2732b2..1c0af96 100644 --- a/chrome/browser/plugin_service.h +++ b/chrome/browser/plugin_service.h @@ -20,6 +20,7 @@ #include "base/synchronization/waitable_event_watcher.h" #include "build/build_config.h" #include "chrome/browser/plugin_process_host.h" +#include "chrome/browser/ppapi_plugin_process_host.h" #include "chrome/common/notification_observer.h" #include "chrome/common/notification_registrar.h" #include "googleurl/src/gurl.h" @@ -46,8 +47,9 @@ class Message; } class MessageLoop; -class Profile; +struct PepperPluginInfo; class PluginDirWatcherDelegate; +class Profile; class ResourceDispatcherHost; namespace net { @@ -89,22 +91,28 @@ class PluginService // Returns the plugin process host corresponding to the plugin process that // has been started by this service. Returns NULL if no process has been // started. - PluginProcessHost* FindPluginProcess(const FilePath& plugin_path); + PluginProcessHost* FindNpapiPluginProcess(const FilePath& plugin_path); + PpapiPluginProcessHost* FindPpapiPluginProcess(const FilePath& plugin_path); // Returns the plugin process host corresponding to the plugin process that // has been started by this service. This will start a process to host the // 'plugin_path' if needed. If the process fails to start, the return value // is NULL. Must be called on the IO thread. - PluginProcessHost* FindOrStartPluginProcess(const FilePath& plugin_path); + PluginProcessHost* FindOrStartNpapiPluginProcess( + const FilePath& plugin_path); + PpapiPluginProcessHost* FindOrStartPpapiPluginProcess( + const FilePath& plugin_path); // Opens a channel to a plugin process for the given mime type, starting // a new plugin process if necessary. This must be called on the IO thread // or else a deadlock can occur. - void OpenChannelToPlugin(int render_process_id, - int render_view_id, - const GURL& url, - const std::string& mime_type, - PluginProcessHost::Client* client); + void OpenChannelToNpapiPlugin(int render_process_id, + int render_view_id, + const GURL& url, + const std::string& mime_type, + PluginProcessHost::Client* client); + void OpenChannelToPpapiPlugin(const FilePath& path, + PpapiPluginProcessHost::Client* client); // Gets the first allowed plugin in the list of plugins that matches // the given url and mime type. Must be called on the FILE thread. @@ -170,10 +178,6 @@ class PluginService FilePathWatcher::Delegate* delegate); #endif - // mapping between plugin path and PluginProcessHost - typedef base::hash_map<FilePath, PluginProcessHost*> PluginMap; - PluginMap plugin_hosts_; - // The main thread's message loop. MessageLoop* main_message_loop_; @@ -212,6 +216,8 @@ class PluginService scoped_refptr<PluginDirWatcherDelegate> file_watcher_delegate_; #endif + std::vector<PepperPluginInfo> ppapi_plugins_; + // Set to true if chrome plugins are enabled. Defaults to true. static bool enable_chrome_plugins_; diff --git a/chrome/browser/plugin_service_browsertest.cc b/chrome/browser/plugin_service_browsertest.cc index 3f31159..ee1be0c 100644 --- a/chrome/browser/plugin_service_browsertest.cc +++ b/chrome/browser/plugin_service_browsertest.cc @@ -72,19 +72,19 @@ IN_PROC_BROWSER_TEST_F(PluginServiceTest, StartAndFindPluginProcess) { // Try to load the default plugin and if this is successful consecutive // calls to FindPluginProcess should return non-zero values. PluginProcessHost* default_plugin_process_host = - plugin_service_->FindOrStartPluginProcess( + plugin_service_->FindOrStartNpapiPluginProcess( FilePath(webkit::npapi::kDefaultPluginLibraryName)); - EXPECT_EQ(default_plugin_process_host, plugin_service_->FindPluginProcess( - FilePath(webkit::npapi::kDefaultPluginLibraryName))); + EXPECT_EQ(default_plugin_process_host, + plugin_service_->FindNpapiPluginProcess( + FilePath(webkit::npapi::kDefaultPluginLibraryName))); } IN_PROC_BROWSER_TEST_F(PluginServiceTest, OpenChannelToPlugin) { MockPluginProcessHostClient mock_client; EXPECT_CALL(mock_client, SetPluginInfo(testing::_)).Times(1); - plugin_service_->OpenChannelToPlugin(0, 0, GURL("http://google.com/"), - "audio/mp3", - &mock_client); + plugin_service_->OpenChannelToNpapiPlugin(0, 0, GURL("http://google.com/"), + "audio/mp3", &mock_client); message_loop_.RunAllPending(); } diff --git a/chrome/browser/ppapi_plugin_process_host.cc b/chrome/browser/ppapi_plugin_process_host.cc index 225cc80..c319dd0 100644 --- a/chrome/browser/ppapi_plugin_process_host.cc +++ b/chrome/browser/ppapi_plugin_process_host.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Copyright (c) 2011 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -7,40 +7,36 @@ #include "base/command_line.h" #include "base/file_path.h" #include "base/process_util.h" +#include "chrome/browser/plugin_service.h" #include "chrome/browser/renderer_host/render_message_filter.h" #include "chrome/common/chrome_switches.h" #include "chrome/common/render_messages.h" #include "ipc/ipc_switches.h" #include "ppapi/proxy/ppapi_messages.h" -PpapiPluginProcessHost::PpapiPluginProcessHost(RenderMessageFilter* filter) - : BrowserChildProcessHost(ChildProcessInfo::PPAPI_PLUGIN_PROCESS, - filter->resource_dispatcher_host()), - filter_(filter) { +PpapiPluginProcessHost::PpapiPluginProcessHost() + : BrowserChildProcessHost( + ChildProcessInfo::PPAPI_PLUGIN_PROCESS, + PluginService::GetInstance()->resource_dispatcher_host()) { } PpapiPluginProcessHost::~PpapiPluginProcessHost() { + CancelRequests(); } -void PpapiPluginProcessHost::Init(const FilePath& path, - IPC::Message* reply_msg) { +bool PpapiPluginProcessHost::Init(const FilePath& path) { plugin_path_ = path; - reply_msg_.reset(reply_msg); - if (!CreateChannel()) { - ReplyToRenderer(base::kNullProcessHandle, IPC::ChannelHandle()); - return; - } + if (!CreateChannel()) + return false; const CommandLine& browser_command_line = *CommandLine::ForCurrentProcess(); CommandLine::StringType plugin_launcher = browser_command_line.GetSwitchValueNative(switches::kPpapiPluginLauncher); FilePath exe_path = ChildProcessHost::GetChildPath(plugin_launcher.empty()); - if (exe_path.empty()) { - ReplyToRenderer(base::kNullProcessHandle, IPC::ChannelHandle()); - return; - } + if (exe_path.empty()) + return false; CommandLine* cmd_line = new CommandLine(exe_path); cmd_line->AppendSwitchASCII(switches::kProcessType, @@ -60,6 +56,36 @@ void PpapiPluginProcessHost::Init(const FilePath& path, base::environment_vector(), #endif cmd_line); + return true; +} + +void PpapiPluginProcessHost::OpenChannelToPlugin(Client* client) { + if (opening_channel()) { + // The channel is already in the process of being opened. Put + // this "open channel" request into a queue of requests that will + // be run once the channel is open. + pending_requests_.push_back(client); + return; + } + + // We already have an open channel, send a request right away to plugin. + RequestPluginChannel(client); +} + +void PpapiPluginProcessHost::RequestPluginChannel(Client* client) { + base::ProcessHandle process_handle; + int renderer_id; + client->GetChannelInfo(&process_handle, &renderer_id); + + // We can't send any sync messages from the browser because it might lead to + // a hang. See the similar code in PluginProcessHost for more description. + PpapiMsg_CreateChannel* msg = new PpapiMsg_CreateChannel(process_handle, + renderer_id); + msg->set_unblock(true); + if (Send(msg)) + sent_requests_.push(client); + else + client->OnChannelOpened(base::kNullProcessHandle, IPC::ChannelHandle()); } bool PpapiPluginProcessHost::CanShutdown() { @@ -72,56 +98,77 @@ void PpapiPluginProcessHost::OnProcessLaunched() { bool PpapiPluginProcessHost::OnMessageReceived(const IPC::Message& msg) { bool handled = true; IPC_BEGIN_MESSAGE_MAP(PpapiPluginProcessHost, msg) - IPC_MESSAGE_HANDLER(PpapiHostMsg_PluginLoaded, OnPluginLoaded) + IPC_MESSAGE_HANDLER(PpapiHostMsg_ChannelCreated, + OnRendererPluginChannelCreated) IPC_MESSAGE_UNHANDLED(handled = false) IPC_END_MESSAGE_MAP() DCHECK(handled); return handled; } +// Called when the browser <--> plugin channel has been established. void PpapiPluginProcessHost::OnChannelConnected(int32 peer_pid) { -#if defined(OS_WIN) - base::ProcessHandle plugins_renderer_handle = NULL; - ::DuplicateHandle(::GetCurrentProcess(), filter_->peer_handle(), - GetChildProcessHandle(), &plugins_renderer_handle, - 0, FALSE, DUPLICATE_SAME_ACCESS); -#elif defined(OS_POSIX) - base::ProcessHandle plugins_renderer_handle = filter_->peer_handle(); -#endif - - PpapiMsg_LoadPlugin* msg = new PpapiMsg_LoadPlugin( - plugins_renderer_handle, plugin_path_, filter_->render_process_id()); - if (!Send(msg)) // Just send an empty handle on failure. - ReplyToRenderer(base::kNullProcessHandle, IPC::ChannelHandle()); - // This function will result in OnChannelCreated getting called to finish. + // This will actually load the plugin. Errors will actually not be reported + // back at this point. Instead, the plugin will fail to establish the + // connections when we request them on behalf of the renderer(s). + Send(new PpapiMsg_LoadPlugin(plugin_path_)); + + // Process all pending channel requests from the renderers. + for (size_t i = 0; i < pending_requests_.size(); i++) + RequestPluginChannel(pending_requests_[i]); + pending_requests_.clear(); } +// Called when the browser <--> plugin channel has an error. This normally +// means the plugin has crashed. void PpapiPluginProcessHost::OnChannelError() { - if (reply_msg_.get()) - ReplyToRenderer(base::kNullProcessHandle, IPC::ChannelHandle()); + // We don't need to notify the renderers that were communicating with the + // plugin since they have their own channels which will go into the error + // state at the same time. Instead, we just need to notify any renderers + // that have requested a connection but have not yet received one. + CancelRequests(); +} + +void PpapiPluginProcessHost::CancelRequests() { + for (size_t i = 0; i < pending_requests_.size(); i++) { + pending_requests_[i]->OnChannelOpened(base::kNullProcessHandle, + IPC::ChannelHandle()); + } + pending_requests_.clear(); + + while (!sent_requests_.empty()) { + sent_requests_.front()->OnChannelOpened(base::kNullProcessHandle, + IPC::ChannelHandle()); + sent_requests_.pop(); + } } -void PpapiPluginProcessHost::OnPluginLoaded( +// Called when a new plugin <--> renderer channel has been created. +void PpapiPluginProcessHost::OnRendererPluginChannelCreated( const IPC::ChannelHandle& channel_handle) { + if (sent_requests_.empty()) + return; + + // All requests should be processed FIFO, so the next item in the + // sent_requests_ queue should be the one that the plugin just created. + Client* client = sent_requests_.front(); + sent_requests_.pop(); + + // Prepare the handle to send to the renderer. base::ProcessHandle plugin_process = GetChildProcessHandle(); #if defined(OS_WIN) + base::ProcessHandle renderer_process; + int renderer_id; + client->GetChannelInfo(&renderer_process, &renderer_id); + base::ProcessHandle renderers_plugin_handle = NULL; ::DuplicateHandle(::GetCurrentProcess(), plugin_process, - filter_->peer_handle(), &renderers_plugin_handle, + renderer_process, &renderers_plugin_handle, 0, FALSE, DUPLICATE_SAME_ACCESS); #elif defined(OS_POSIX) // Don't need to duplicate anything on POSIX since it's just a PID. base::ProcessHandle renderers_plugin_handle = plugin_process; #endif - ReplyToRenderer(renderers_plugin_handle, channel_handle); -} -void PpapiPluginProcessHost::ReplyToRenderer( - base::ProcessHandle plugin_handle, - const IPC::ChannelHandle& channel_handle) { - DCHECK(reply_msg_.get()); - ViewHostMsg_OpenChannelToPepperPlugin::WriteReplyParams(reply_msg_.get(), - plugin_handle, - channel_handle); - filter_->Send(reply_msg_.release()); + client->OnChannelOpened(renderers_plugin_handle, channel_handle); } diff --git a/chrome/browser/ppapi_plugin_process_host.h b/chrome/browser/ppapi_plugin_process_host.h index 227cf31..945195d 100644 --- a/chrome/browser/ppapi_plugin_process_host.h +++ b/chrome/browser/ppapi_plugin_process_host.h @@ -6,20 +6,48 @@ #define CHROME_BROWSER_PPAPI_PLUGIN_PROCESS_HOST_H_ #pragma once +#include <queue> + #include "base/basictypes.h" #include "base/file_path.h" #include "chrome/browser/browser_child_process_host.h" -class RenderMessageFilter; - class PpapiPluginProcessHost : public BrowserChildProcessHost { public: - explicit PpapiPluginProcessHost(RenderMessageFilter* filter); + class Client { + public: + // Gets the information about the renderer that's requesting the channel. + virtual void GetChannelInfo(base::ProcessHandle* renderer_handle, + int* renderer_id) = 0; + + // Called when the channel is asynchronously opened to the plugin or on + // error. On error, the parameters should be: + // base::kNullProcessHandle + // IPC::ChannelHandle() + virtual void OnChannelOpened(base::ProcessHandle plugin_process_handle, + const IPC::ChannelHandle& channel_handle) = 0; + }; + + // You must call init before doing anything else. + explicit PpapiPluginProcessHost(); virtual ~PpapiPluginProcessHost(); - void Init(const FilePath& path, IPC::Message* reply_msg); + // Actually launches the process with the given plugin path. Returns true + // on success (the process was spawned). + bool Init(const FilePath& path); + + // Opens a new channel to the plugin. The client will be notified when the + // channel is ready or if there's an error. + void OpenChannelToPlugin(Client* client); + + const FilePath& plugin_path() const { return plugin_path_; } + + // The client pointer must remain valid until its callback is issued. private: + + void RequestPluginChannel(Client* client); + virtual bool CanShutdown(); virtual void OnProcessLaunched(); @@ -27,22 +55,22 @@ class PpapiPluginProcessHost : public BrowserChildProcessHost { virtual void OnChannelConnected(int32 peer_pid); virtual void OnChannelError(); + void CancelRequests(); + // IPC message handlers. - void OnPluginLoaded(const IPC::ChannelHandle& handle); + void OnRendererPluginChannelCreated(const IPC::ChannelHandle& handle); - // Sends the reply_msg_ to the renderer with the given channel info. - void ReplyToRenderer(base::ProcessHandle plugin_handle, - const IPC::ChannelHandle& channel_handle); + // Channel requests that we are waiting to send to the plugin process once + // the channel is opened. + std::vector<Client*> pending_requests_; - RenderMessageFilter* filter_; + // Channel requests that we have already sent to the plugin process, but + // haven't heard back about yet. + std::queue<Client*> sent_requests_; // Path to the plugin library. FilePath plugin_path_; - // When we're waiting for initialization of the plugin, this contains the - // reply message for the renderer to tell it that it can continue. - scoped_ptr<IPC::Message> reply_msg_; - DISALLOW_COPY_AND_ASSIGN(PpapiPluginProcessHost); }; diff --git a/chrome/browser/renderer_host/render_message_filter.cc b/chrome/browser/renderer_host/render_message_filter.cc index d9d7a32..f4f8514 100644 --- a/chrome/browser/renderer_host/render_message_filter.cc +++ b/chrome/browser/renderer_host/render_message_filter.cc @@ -167,39 +167,62 @@ void RenderParamsFromPrintSettings(const printing::PrintSettings& settings, params->supports_alpha_blend = settings.supports_alpha_blend(); } -class ClearCacheCompletion : public net::CompletionCallback { +// Common functionality for converting a sync renderer message to a callback +// function in the browser. Derive from this, create it on the heap when +// issuing your callback. When done, write your reply parameters into +// reply_msg(), and then call SendReplyAndDeleteThis(). +class RenderMessageCompletionCallback { public: - ClearCacheCompletion(IPC::Message* reply_msg, - RenderMessageFilter* filter) - : reply_msg_(reply_msg), - filter_(filter) { + RenderMessageCompletionCallback(RenderMessageFilter* filter, + IPC::Message* reply_msg) + : filter_(filter), + reply_msg_(reply_msg) { } - virtual void RunWithParams(const Tuple1<int>& params) { - ViewHostMsg_ClearCache::WriteReplyParams(reply_msg_, params.a); + virtual ~RenderMessageCompletionCallback() { + } + + RenderMessageFilter* filter() { return filter_.get(); } + IPC::Message* reply_msg() { return reply_msg_; } + + void SendReplyAndDeleteThis() { filter_->Send(reply_msg_); delete this; } private: - IPC::Message* reply_msg_; scoped_refptr<RenderMessageFilter> filter_; + IPC::Message* reply_msg_; }; -class OpenChannelToPluginCallback : public PluginProcessHost::Client { +class ClearCacheCompletion : public RenderMessageCompletionCallback, + public net::CompletionCallback { public: - OpenChannelToPluginCallback(RenderMessageFilter* filter, - IPC::Message* reply_msg) - : filter_(filter), - reply_msg_(reply_msg) { + ClearCacheCompletion(RenderMessageFilter* filter, + IPC::Message* reply_msg) + : RenderMessageCompletionCallback(filter, reply_msg) { + } + + virtual void RunWithParams(const Tuple1<int>& params) { + ViewHostMsg_ClearCache::WriteReplyParams(reply_msg(), params.a); + SendReplyAndDeleteThis(); + } +}; + +class OpenChannelToNpapiPluginCallback : public RenderMessageCompletionCallback, + public PluginProcessHost::Client { + public: + OpenChannelToNpapiPluginCallback(RenderMessageFilter* filter, + IPC::Message* reply_msg) + : RenderMessageCompletionCallback(filter, reply_msg) { } virtual int ID() { - return filter_->render_process_id(); + return filter()->render_process_id(); } virtual bool OffTheRecord() { - return filter_->off_the_record(); + return filter()->off_the_record(); } virtual void SetPluginInfo(const webkit::npapi::WebPluginInfo& info) { @@ -207,27 +230,45 @@ class OpenChannelToPluginCallback : public PluginProcessHost::Client { } virtual void OnChannelOpened(const IPC::ChannelHandle& handle) { - WriteReply(handle); + WriteReplyAndDeleteThis(handle); } virtual void OnError() { - WriteReply(IPC::ChannelHandle()); + WriteReplyAndDeleteThis(IPC::ChannelHandle()); } private: - void WriteReply(const IPC::ChannelHandle& handle) { - ViewHostMsg_OpenChannelToPlugin::WriteReplyParams(reply_msg_, - handle, - info_); - filter_->Send(reply_msg_); - delete this; + void WriteReplyAndDeleteThis(const IPC::ChannelHandle& handle) { + ViewHostMsg_OpenChannelToPlugin::WriteReplyParams(reply_msg(), + handle, info_); + SendReplyAndDeleteThis(); } - scoped_refptr<RenderMessageFilter> filter_; - IPC::Message* reply_msg_; webkit::npapi::WebPluginInfo info_; }; +class OpenChannelToPpapiPluginCallback : public RenderMessageCompletionCallback, + public PpapiPluginProcessHost::Client { + public: + OpenChannelToPpapiPluginCallback(RenderMessageFilter* filter, + IPC::Message* reply_msg) + : RenderMessageCompletionCallback(filter, reply_msg) { + } + + virtual void GetChannelInfo(base::ProcessHandle* renderer_handle, + int* renderer_id) { + *renderer_handle = filter()->peer_handle(); + *renderer_id = filter()->render_process_id(); + } + + virtual void OnChannelOpened(base::ProcessHandle plugin_process_handle, + const IPC::ChannelHandle& channel_handle) { + ViewHostMsg_OpenChannelToPepperPlugin::WriteReplyParams( + reply_msg(), plugin_process_handle, channel_handle); + SendReplyAndDeleteThis(); + } +}; + } // namespace RenderMessageFilter::RenderMessageFilter( @@ -719,17 +760,16 @@ void RenderMessageFilter::OnOpenChannelToPlugin(int routing_id, const GURL& url, const std::string& mime_type, IPC::Message* reply_msg) { - plugin_service_->OpenChannelToPlugin( + plugin_service_->OpenChannelToNpapiPlugin( render_process_id_, routing_id, url, mime_type, - new OpenChannelToPluginCallback(this, reply_msg)); + new OpenChannelToNpapiPluginCallback(this, reply_msg)); } void RenderMessageFilter::OnOpenChannelToPepperPlugin( const FilePath& path, IPC::Message* reply_msg) { - PpapiPluginProcessHost* host = new PpapiPluginProcessHost(this); - host->Init(path, reply_msg); - ppapi_plugin_hosts_.push_back(linked_ptr<PpapiPluginProcessHost>(host)); + plugin_service_->OpenChannelToPpapiPlugin( + path, new OpenChannelToPpapiPluginCallback(this, reply_msg)); } void RenderMessageFilter::OnLaunchNaCl( @@ -1293,7 +1333,7 @@ void RenderMessageFilter::OnClearCache(IPC::Message* reply_msg) { http_transaction_factory()->GetCache()->GetCurrentBackend(); if (backend) { ClearCacheCompletion* callback = - new ClearCacheCompletion(reply_msg, this); + new ClearCacheCompletion(this, reply_msg); rv = backend->DoomAllEntries(callback); if (rv == net::ERR_IO_PENDING) { // The callback will send the reply. diff --git a/chrome/browser/renderer_host/render_message_filter.h b/chrome/browser/renderer_host/render_message_filter.h index d2901715..a0339de 100644 --- a/chrome/browser/renderer_host/render_message_filter.h +++ b/chrome/browser/renderer_host/render_message_filter.h @@ -34,7 +34,6 @@ struct FontDescriptor; class HostContentSettingsMap; class HostZoomMap; class NotificationsPrefsCache; -class PpapiPluginProcessHost; class Profile; class RenderWidgetHelper; class URLRequestContextGetter; @@ -410,9 +409,6 @@ class RenderMessageFilter : public BrowserMessageFilter, base::TimeTicks last_plugin_refresh_time_; // Initialized to 0. - // A list of all Ppapi plugin processes for this renderer. - std::vector<linked_ptr<PpapiPluginProcessHost> > ppapi_plugin_hosts_; - scoped_refptr<WebKitContext> webkit_context_; int render_process_id_; diff --git a/chrome/ppapi_plugin/ppapi_thread.cc b/chrome/ppapi_plugin/ppapi_thread.cc index 4e8c0c9..483863c 100644 --- a/chrome/ppapi_plugin/ppapi_thread.cc +++ b/chrome/ppapi_plugin/ppapi_thread.cc @@ -4,27 +4,33 @@ #include "chrome/ppapi_plugin/ppapi_thread.h" +#include <limits> + #include "base/process_util.h" +#include "base/rand_util.h" #include "chrome/common/child_process.h" #include "ipc/ipc_channel_handle.h" #include "ipc/ipc_sync_channel.h" +#include "ppapi/c/pp_errors.h" #include "ppapi/c/ppp.h" #include "ppapi/proxy/plugin_dispatcher.h" #include "ppapi/proxy/ppapi_messages.h" -#if defined(OS_POSIX) -#include "base/eintr_wrapper.h" -#include "ipc/ipc_channel_posix.h" -#endif - PpapiThread::PpapiThread() -#if defined(OS_POSIX) - : renderer_fd_(-1) -#endif - { + : get_plugin_interface_(NULL), + local_pp_module_( + base::RandInt(0, std::numeric_limits<PP_Module>::max())) { } PpapiThread::~PpapiThread() { + if (library_.is_valid()) { + // The ShutdownModule function is optional. + pp::proxy::Dispatcher::ShutdownModuleFunc shutdown_module = + reinterpret_cast<pp::proxy::Dispatcher::ShutdownModuleFunc>( + library_.GetFunctionPointer("PPP_ShutdownModule")); + if (shutdown_module) + shutdown_module(); + } } // The "regular" ChildThread implements this function and does some standard @@ -37,37 +43,23 @@ PpapiThread::~PpapiThread() { bool PpapiThread::OnMessageReceived(const IPC::Message& msg) { IPC_BEGIN_MESSAGE_MAP(PpapiThread, msg) IPC_MESSAGE_HANDLER(PpapiMsg_LoadPlugin, OnMsgLoadPlugin) + IPC_MESSAGE_HANDLER(PpapiMsg_CreateChannel, OnMsgCreateChannel) IPC_END_MESSAGE_MAP() return true; } -void PpapiThread::OnMsgLoadPlugin(base::ProcessHandle host_process_handle, - const FilePath& path, - int renderer_id) { - IPC::ChannelHandle channel_handle; - if (!LoadPluginLib(host_process_handle, path) || - !SetupRendererChannel(renderer_id, &channel_handle)) { - // An empty channel handle indicates error. - Send(new PpapiHostMsg_PluginLoaded(IPC::ChannelHandle())); - return; - } - - Send(new PpapiHostMsg_PluginLoaded(channel_handle)); -} - -bool PpapiThread::LoadPluginLib(base::ProcessHandle host_process_handle, - const FilePath& path) { +void PpapiThread::OnMsgLoadPlugin(const FilePath& path) { base::ScopedNativeLibrary library(base::LoadNativeLibrary(path)); if (!library.is_valid()) - return false; + return; // Get the GetInterface function (required). - pp::proxy::Dispatcher::GetInterfaceFunc get_interface = + get_plugin_interface_ = reinterpret_cast<pp::proxy::Dispatcher::GetInterfaceFunc>( library.GetFunctionPointer("PPP_GetInterface")); - if (!get_interface) { + if (!get_plugin_interface_) { LOG(WARNING) << "No PPP_GetInterface in plugin library"; - return false; + return; } // Get the InitializeModule function (required). @@ -76,26 +68,42 @@ bool PpapiThread::LoadPluginLib(base::ProcessHandle host_process_handle, library.GetFunctionPointer("PPP_InitializeModule")); if (!init_module) { LOG(WARNING) << "No PPP_InitializeModule in plugin library"; - return false; + return; + } + int32_t init_error = init_module( + local_pp_module_, + &pp::proxy::PluginDispatcher::GetInterfaceFromDispatcher); + if (init_error != PP_OK) { + LOG(WARNING) << "InitModule failed with error " << init_error; + return; } - - // Get the ShutdownModule function (optional). - pp::proxy::Dispatcher::ShutdownModuleFunc shutdown_module = - reinterpret_cast<pp::proxy::Dispatcher::ShutdownModuleFunc>( - library.GetFunctionPointer("PPP_ShutdownModule")); library_.Reset(library.Release()); - dispatcher_.reset(new pp::proxy::PluginDispatcher( - host_process_handle, get_interface, init_module, shutdown_module)); - return true; } -bool PpapiThread::SetupRendererChannel(int renderer_id, +void PpapiThread::OnMsgCreateChannel(base::ProcessHandle host_process_handle, + int renderer_id) { + IPC::ChannelHandle channel_handle; + if (!library_.is_valid() || // Plugin couldn't be loaded. + !SetupRendererChannel(host_process_handle, renderer_id, + &channel_handle)) { + Send(new PpapiHostMsg_ChannelCreated(IPC::ChannelHandle())); + return; + } + + Send(new PpapiHostMsg_ChannelCreated(channel_handle)); +} + +bool PpapiThread::SetupRendererChannel(base::ProcessHandle host_process_handle, + int renderer_id, IPC::ChannelHandle* handle) { + pp::proxy::PluginDispatcher* dispatcher = new pp::proxy::PluginDispatcher( + host_process_handle, get_plugin_interface_); + IPC::ChannelHandle plugin_handle; plugin_handle.name = StringPrintf("%d.r%d", base::GetCurrentProcId(), renderer_id); - if (!dispatcher_->InitWithChannel( + if (!dispatcher->InitWithChannel( ChildProcess::current()->io_message_loop(), plugin_handle, false, ChildProcess::current()->GetShutDownEvent())) @@ -104,18 +112,9 @@ bool PpapiThread::SetupRendererChannel(int renderer_id, handle->name = plugin_handle.name; #if defined(OS_POSIX) // On POSIX, pass the renderer-side FD. - renderer_fd_ = dispatcher_->channel()->GetClientFileDescriptor(); - handle->socket = base::FileDescriptor(renderer_fd_, false); + handle->socket = base::FileDescriptor(dispatcher->GetRendererFD(), false); #endif + return true; } -#if defined(OS_POSIX) -void PpapiThread::CloseRendererFD() { - if (renderer_fd_ != -1) { - if (HANDLE_EINTR(close(renderer_fd_)) < 0) - PLOG(ERROR) << "close"; - renderer_fd_ = -1; - } -} -#endif diff --git a/chrome/ppapi_plugin/ppapi_thread.h b/chrome/ppapi_plugin/ppapi_thread.h index 08e62fc..32e45f5 100644 --- a/chrome/ppapi_plugin/ppapi_thread.h +++ b/chrome/ppapi_plugin/ppapi_thread.h @@ -12,6 +12,8 @@ #include "base/scoped_ptr.h" #include "build/build_config.h" #include "chrome/common/child_thread.h" +#include "ppapi/c/pp_module.h" +#include "ppapi/proxy/dispatcher.h" class FilePath; @@ -35,35 +37,27 @@ class PpapiThread : public ChildThread { virtual bool OnMessageReceived(const IPC::Message& msg); // Message handlers. - void OnMsgLoadPlugin(base::ProcessHandle renderer_handle, - const FilePath& path, - int renderer_id); - - bool LoadPluginLib(base::ProcessHandle host_process_handle, - const FilePath& path); + void OnMsgLoadPlugin(const FilePath& path); + void OnMsgCreateChannel(base::ProcessHandle host_process_handle, + int renderer_id); // Sets up the channel to the given renderer. On success, returns true and // fills the given ChannelHandle with the information from the new channel. - bool SetupRendererChannel(int renderer_id, + bool SetupRendererChannel(base::ProcessHandle host_process_handle, + int renderer_id, IPC::ChannelHandle* handle); -#if defined(OS_POSIX) - // Close the plugin process' copy of the renderer's side of the plugin - // channel. This can be called after the renderer is known to have its own - // copy of renderer_fd_. - void CloseRendererFD(); -#endif - base::ScopedNativeLibrary library_; - scoped_ptr<pp::proxy::PluginDispatcher> dispatcher_; + pp::proxy::Dispatcher::GetInterfaceFunc get_plugin_interface_; -#if defined(OS_POSIX) - // FD for the renderer end of the socket. It is closed when the IPC layer - // indicates that the channel is connected, proving that the renderer has - // access to its side of the socket. - int renderer_fd_; -#endif + // Local concept of the module ID. Some functions take this. It's necessary + // for the in-process PPAPI to handle this properly, but for proxied it's + // unnecessary. The proxy talking to multiple renderers means that each + // renderer has a different idea of what the module ID is for this plugin. + // To force people to "do the right thing" we generate a random module ID + // and pass it around as necessary. + PP_Module local_pp_module_; DISALLOW_COPY_AND_ASSIGN(PpapiThread); }; diff --git a/chrome/renderer/pepper_plugin_delegate_impl.cc b/chrome/renderer/pepper_plugin_delegate_impl.cc index 87c8625..fd4a2dd 100644 --- a/chrome/renderer/pepper_plugin_delegate_impl.cc +++ b/chrome/renderer/pepper_plugin_delegate_impl.cc @@ -365,12 +365,6 @@ bool DispatcherWrapper::Init( dispatcher_.reset(); return false; } - - if (!dispatcher_->InitializeModule()) { - // TODO(brettw) does the module get unloaded in this case? - dispatcher_.reset(); - return false; - } return true; } |