diff options
-rw-r--r-- | chrome/browser/plugin_data_remover.cc | 8 | ||||
-rw-r--r-- | chrome/browser/plugin_data_remover.h | 17 | ||||
-rw-r--r-- | content/browser/plugin_process_host.cc | 12 | ||||
-rw-r--r-- | content/browser/plugin_process_host.h | 4 | ||||
-rw-r--r-- | content/browser/plugin_service.cc | 22 | ||||
-rw-r--r-- | content/browser/plugin_service.h | 5 | ||||
-rw-r--r-- | content/browser/plugin_service_browsertest.cc | 62 | ||||
-rw-r--r-- | content/browser/renderer_host/render_message_filter.cc | 139 | ||||
-rw-r--r-- | content/browser/renderer_host/render_message_filter.h | 9 |
9 files changed, 215 insertions, 63 deletions
diff --git a/chrome/browser/plugin_data_remover.cc b/chrome/browser/plugin_data_remover.cc index 5b06586..e33a00c 100644 --- a/chrome/browser/plugin_data_remover.cc +++ b/chrome/browser/plugin_data_remover.cc @@ -24,9 +24,9 @@ namespace { -const char* kFlashMimeType = "application/x-shockwave-flash"; +const char kFlashMimeType[] = "application/x-shockwave-flash"; // The minimum Flash Player version that implements NPP_ClearSiteData. -const char* kMinFlashVersion = "10.3"; +const char kMinFlashVersion[] = "10.3"; const int64 kRemovalTimeoutMs = 10000; const uint64 kClearAllData = 0; @@ -97,6 +97,10 @@ void PluginDataRemover::SetPluginInfo( const webkit::WebPluginInfo& info) { } +void PluginDataRemover::OnFoundPluginProcessHost( + PluginProcessHost* host) { +} + void PluginDataRemover::OnChannelOpened(const IPC::ChannelHandle& handle) { ConnectToChannel(handle); // Balancing the AddRef call in StartRemoving. diff --git a/chrome/browser/plugin_data_remover.h b/chrome/browser/plugin_data_remover.h index 9ff4235..c97a191 100644 --- a/chrome/browser/plugin_data_remover.h +++ b/chrome/browser/plugin_data_remover.h @@ -47,16 +47,17 @@ class PluginDataRemover : public base::RefCountedThreadSafe<PluginDataRemover>, void Wait(); // PluginProcessHost::Client methods. - virtual int ID(); - virtual bool OffTheRecord(); - virtual const content::ResourceContext& GetResourceContext(); - virtual void SetPluginInfo(const webkit::WebPluginInfo& info); - virtual void OnChannelOpened(const IPC::ChannelHandle& handle); - virtual void OnError(); + virtual int ID() OVERRIDE; + virtual bool OffTheRecord() OVERRIDE; + virtual const content::ResourceContext& GetResourceContext() OVERRIDE; + virtual void SetPluginInfo(const webkit::WebPluginInfo& info) OVERRIDE; + virtual void OnFoundPluginProcessHost(PluginProcessHost* host) OVERRIDE; + virtual void OnChannelOpened(const IPC::ChannelHandle& handle) OVERRIDE; + virtual void OnError() OVERRIDE; // IPC::Channel::Listener methods. - virtual bool OnMessageReceived(const IPC::Message& message); - virtual void OnChannelError(); + virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE; + virtual void OnChannelError() OVERRIDE; private: friend class base::RefCountedThreadSafe<PluginDataRemover>; diff --git a/content/browser/plugin_process_host.cc b/content/browser/plugin_process_host.cc index 23516c7..973be60 100644 --- a/content/browser/plugin_process_host.cc +++ b/content/browser/plugin_process_host.cc @@ -364,6 +364,18 @@ void PluginProcessHost::OpenChannelToPlugin(Client* client) { RequestPluginChannel(client); } +void PluginProcessHost::CancelRequest(Client* client) { + std::vector<Client*>::iterator it = pending_requests_.begin(); + while (it != pending_requests_.end()) { + if (client == *it) { + pending_requests_.erase(it); + return; + } + ++it; + } + DCHECK(it != pending_requests_.end()); +} + void PluginProcessHost::RequestPluginChannel(Client* client) { // We can't send any sync messages from the browser because it might lead to // a hang. However this async messages must be answered right away by the diff --git a/content/browser/plugin_process_host.h b/content/browser/plugin_process_host.h index fcec36c..202c9a9 100644 --- a/content/browser/plugin_process_host.h +++ b/content/browser/plugin_process_host.h @@ -52,6 +52,7 @@ class PluginProcessHost : public BrowserChildProcessHost { virtual const content::ResourceContext& GetResourceContext() = 0; virtual bool OffTheRecord() = 0; virtual void SetPluginInfo(const webkit::WebPluginInfo& info) = 0; + virtual void OnFoundPluginProcessHost(PluginProcessHost* host) = 0; // The client should delete itself when one of these methods is called. virtual void OnChannelOpened(const IPC::ChannelHandle& handle) = 0; virtual void OnError() = 0; @@ -83,6 +84,9 @@ class PluginProcessHost : public BrowserChildProcessHost { static void CancelPendingRequestsForResourceContext( const content::ResourceContext* context); + // This function is called to cancel requests to open new channels. + void CancelRequest(Client* client); + // This function is called on the IO thread once we receive a reply from the // modal HTML dialog (in the form of a JSON string). This function forwards // that reply back to the plugin that requested the dialog. diff --git a/content/browser/plugin_service.cc b/content/browser/plugin_service.cc index 86670db..daa0c62 100644 --- a/content/browser/plugin_service.cc +++ b/content/browser/plugin_service.cc @@ -100,6 +100,8 @@ PluginService::~PluginService() { if (hklm_event_.get()) hklm_event_->Release(); #endif + // Make sure no plugin channel requests have been leaked. + DCHECK(pending_plugin_clients_.empty()); } void PluginService::StartWatchingPlugins() { @@ -285,6 +287,8 @@ void PluginService::OpenChannelToNpapiPlugin( const GURL& page_url, const std::string& mime_type, PluginProcessHost::Client* client) { + DCHECK(!ContainsKey(pending_plugin_clients_, client)); + pending_plugin_clients_.insert(client); // The PluginList::GetPluginInfo may need to load the plugins. Don't do it on // the IO thread. BrowserThread::PostTask( @@ -316,6 +320,13 @@ void PluginService::OpenChannelToPpapiBroker( client->OnChannelOpened(base::kNullProcessHandle, IPC::ChannelHandle()); } +void PluginService::CancelOpenChannelToNpapiPlugin( + PluginProcessHost::Client* client) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + DCHECK(ContainsKey(pending_plugin_clients_, client)); + pending_plugin_clients_.erase(client); +} + void PluginService::GetAllowedPluginForOpenChannelToPlugin( int render_process_id, int render_view_id, @@ -347,11 +358,18 @@ void PluginService::FinishOpenChannelToPlugin( PluginProcessHost::Client* client) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + // Make sure it hasn't been canceled yet. + if (!ContainsKey(pending_plugin_clients_, client)) + return; + pending_plugin_clients_.erase(client); + PluginProcessHost* plugin_host = FindOrStartNpapiPluginProcess(plugin_path); - if (plugin_host) + if (plugin_host) { + client->OnFoundPluginProcessHost(plugin_host); plugin_host->OpenChannelToPlugin(client); - else + } else { client->OnError(); + } } bool PluginService::GetPluginInfo(int render_process_id, diff --git a/content/browser/plugin_service.h b/content/browser/plugin_service.h index 8dbef70f..c75f467 100644 --- a/content/browser/plugin_service.h +++ b/content/browser/plugin_service.h @@ -98,6 +98,9 @@ class PluginService void OpenChannelToPpapiBroker(const FilePath& path, PpapiBrokerProcessHost::Client* client); + // Cancels opening a channel to a NPAPI plugin. + void CancelOpenChannelToNpapiPlugin(PluginProcessHost::Client* client); + // Gets the plugin in the list of plugins that matches the given url and mime // type. Must be called on the FILE thread if |use_stale| is NULL. bool GetPluginInfo(int render_process_id, @@ -192,6 +195,8 @@ class PluginService // Weak pointer; outlives us. content::PluginServiceFilter* filter_; + std::set<PluginProcessHost::Client*> pending_plugin_clients_; + DISALLOW_COPY_AND_ASSIGN(PluginService); }; diff --git a/content/browser/plugin_service_browsertest.cc b/content/browser/plugin_service_browsertest.cc index c49a90b..05f4b2f 100644 --- a/content/browser/plugin_service_browsertest.cc +++ b/content/browser/plugin_service_browsertest.cc @@ -4,6 +4,7 @@ #include "content/browser/plugin_service.h" +#include "base/bind.h" #include "base/command_line.h" #include "base/path_service.h" #include "chrome/browser/ui/browser.h" @@ -42,6 +43,8 @@ class MockPluginProcessHostClient : public PluginProcessHost::Client, virtual const content::ResourceContext& GetResourceContext() OVERRIDE { return context_; } + virtual void OnFoundPluginProcessHost(PluginProcessHost* host) { + } void OnChannelOpened(const IPC::ChannelHandle& handle) { ASSERT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::IO)); @@ -76,8 +79,6 @@ class MockPluginProcessHostClient : public PluginProcessHost::Client, DISALLOW_COPY_AND_ASSIGN(MockPluginProcessHostClient); }; -} // namespace - class PluginServiceTest : public InProcessBrowserTest { public: PluginServiceTest() : InProcessBrowserTest() { } @@ -101,3 +102,60 @@ IN_PROC_BROWSER_TEST_F(PluginServiceTest, OpenChannelToPlugin) { 0, 0, GURL(), GURL(), kNPAPITestPluginMimeType, &mock_client); ui_test_utils::RunMessageLoop(); } + +// A strict mock that fails if any of the methods are called. They shouldn't be +// called since the request should get canceled before then. +class MockCanceledPluginProcessHostClient: public PluginProcessHost::Client { + public: + // Client implementation. + MOCK_METHOD1(OnFoundPluginProcessHost, void(PluginProcessHost* host)); + MOCK_METHOD1(OnChannelOpened, void(const IPC::ChannelHandle& handle)); + MOCK_METHOD1(SetPluginInfo, void(const webkit::WebPluginInfo& info)); + MOCK_METHOD0(OnError, void()); + + // Listener implementation. + MOCK_METHOD1(OnMessageReceived, bool(const IPC::Message& message)); + MOCK_METHOD1(OnChannelConnected, void(int32 peer_pid)); + MOCK_METHOD0(OnChannelError, void()); + MOCK_METHOD0(OnChannelDenied, void()); + MOCK_METHOD0(OnChannelListenError, void()); + + private: + DISALLOW_COPY_AND_ASSIGN(MockCanceledPluginProcessHostClient); +}; + +void DoNothing() {} + +void QuitUIMessageLoopFromIOThread() { + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, + new MessageLoop::QuitTask()); +} + +void OpenChannelAndThenCancel(PluginProcessHost::Client* client) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + // Start opening the channel + PluginService::GetInstance()->OpenChannelToNpapiPlugin( + 0, 0, GURL(), GURL(), kNPAPITestPluginMimeType, client); + // Immediately cancel it. This is guaranteed to work since PluginService needs + // to consult its filter on the FILE thread. + PluginService::GetInstance()->CancelOpenChannelToNpapiPlugin(client); + // Before we terminate the test, add a roundtrip through the FILE thread to + // make sure that it's had a chance to post back to the IO thread. Then signal + // the UI thread to stop and exit the test. + BrowserThread::PostTaskAndReply( + BrowserThread::FILE, FROM_HERE, + base::Bind(&DoNothing), + base::Bind(&QuitUIMessageLoopFromIOThread)); +} + +// Should not attempt to open a channel, since it should be canceled early on. +IN_PROC_BROWSER_TEST_F(PluginServiceTest, CancelOpenChannelToPlugin) { + ::testing::StrictMock<MockPluginProcessHostClient> mock_client( + browser()->profile()->GetResourceContext()); + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + NewRunnableFunction(OpenChannelAndThenCancel, &mock_client)); + ui_test_utils::RunMessageLoop(); +} + +} // namespace diff --git a/content/browser/renderer_host/render_message_filter.cc b/content/browser/renderer_host/render_message_filter.cc index f6e4547..1135ed8 100644 --- a/content/browser/renderer_host/render_message_filter.cc +++ b/content/browser/renderer_host/render_message_filter.cc @@ -118,51 +118,6 @@ class ClearCacheCompletion : public RenderMessageCompletionCallback, } }; -class OpenChannelToNpapiPluginCallback : public RenderMessageCompletionCallback, - public PluginProcessHost::Client { - public: - OpenChannelToNpapiPluginCallback(RenderMessageFilter* filter, - const content::ResourceContext& context, - IPC::Message* reply_msg) - : RenderMessageCompletionCallback(filter, reply_msg), - context_(context) { - } - - virtual int ID() OVERRIDE { - return filter()->render_process_id(); - } - - virtual const content::ResourceContext& GetResourceContext() OVERRIDE { - return context_; - } - - virtual bool OffTheRecord() OVERRIDE { - return filter()->OffTheRecord(); - } - - virtual void SetPluginInfo(const webkit::WebPluginInfo& info) OVERRIDE { - info_ = info; - } - - virtual void OnChannelOpened(const IPC::ChannelHandle& handle) OVERRIDE { - WriteReplyAndDeleteThis(handle); - } - - virtual void OnError() OVERRIDE { - WriteReplyAndDeleteThis(IPC::ChannelHandle()); - } - - private: - void WriteReplyAndDeleteThis(const IPC::ChannelHandle& handle) { - ViewHostMsg_OpenChannelToPlugin::WriteReplyParams(reply_msg(), - handle, info_); - SendReplyAndDeleteThis(); - } - - const content::ResourceContext& context_; - webkit::WebPluginInfo info_; -}; - class OpenChannelToPpapiPluginCallback : public RenderMessageCompletionCallback, public PpapiPluginProcessHost::Client { public: @@ -278,6 +233,68 @@ class DoomEntriesHelper { } // namespace +class RenderMessageFilter::OpenChannelToNpapiPluginCallback + : public RenderMessageCompletionCallback, + public PluginProcessHost::Client { + public: + OpenChannelToNpapiPluginCallback(RenderMessageFilter* filter, + const content::ResourceContext& context, + IPC::Message* reply_msg) + : RenderMessageCompletionCallback(filter, reply_msg), + context_(context), + host_(NULL) { + } + + virtual int ID() OVERRIDE { + return filter()->render_process_id(); + } + + virtual const content::ResourceContext& GetResourceContext() OVERRIDE { + return context_; + } + + virtual bool OffTheRecord() OVERRIDE { + return filter()->OffTheRecord(); + } + + virtual void SetPluginInfo(const webkit::WebPluginInfo& info) OVERRIDE { + info_ = info; + } + + virtual void OnFoundPluginProcessHost(PluginProcessHost* host) OVERRIDE { + DCHECK(host); + host_ = host; + } + + virtual void OnChannelOpened(const IPC::ChannelHandle& handle) OVERRIDE { + WriteReplyAndDeleteThis(handle); + } + + virtual void OnError() OVERRIDE { + WriteReplyAndDeleteThis(IPC::ChannelHandle()); + } + + PluginProcessHost* host() const { + return host_; + } + + void Cancel() { + delete this; + } + + private: + void WriteReplyAndDeleteThis(const IPC::ChannelHandle& handle) { + ViewHostMsg_OpenChannelToPlugin::WriteReplyParams(reply_msg(), + handle, info_); + filter()->OnCompletedOpenChannelToNpapiPlugin(this); + SendReplyAndDeleteThis(); + } + + const content::ResourceContext& context_; + webkit::WebPluginInfo info_; + PluginProcessHost* host_; +}; + RenderMessageFilter::RenderMessageFilter( int render_process_id, PluginService* plugin_service, @@ -302,6 +319,22 @@ RenderMessageFilter::RenderMessageFilter( RenderMessageFilter::~RenderMessageFilter() { // This function should be called on the IO thread. DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + DCHECK(plugin_host_clients_.empty()); +} + +void RenderMessageFilter::OnChannelClosing() { + BrowserMessageFilter::OnChannelClosing(); + for (std::set<OpenChannelToNpapiPluginCallback*>::iterator it = + plugin_host_clients_.begin(); it != plugin_host_clients_.end(); ++it) { + OpenChannelToNpapiPluginCallback* client = *it; + if (client->host()) { + client->host()->CancelRequest(client); + } else { + plugin_service_->CancelOpenChannelToNpapiPlugin(client); + } + client->Cancel(); + } + plugin_host_clients_.clear(); } void RenderMessageFilter::OverrideThreadForMessage(const IPC::Message& message, @@ -568,12 +601,13 @@ void RenderMessageFilter::OnOpenChannelToPlugin(int routing_id, const GURL& policy_url, const std::string& mime_type, IPC::Message* reply_msg) { + OpenChannelToNpapiPluginCallback* client = + new OpenChannelToNpapiPluginCallback(this, resource_context_, reply_msg); + DCHECK(!ContainsKey(plugin_host_clients_, client)); + plugin_host_clients_.insert(client); plugin_service_->OpenChannelToNpapiPlugin( render_process_id_, routing_id, - url, policy_url, mime_type, - new OpenChannelToNpapiPluginCallback(this, - resource_context_, - reply_msg)); + url, policy_url, mime_type, client); } void RenderMessageFilter::OnOpenChannelToPepperPlugin( @@ -916,3 +950,10 @@ void RenderMessageFilter::SendGetRawCookiesResponse( ViewHostMsg_GetRawCookies::WriteReplyParams(reply_msg, cookies); Send(reply_msg); } + +void RenderMessageFilter::OnCompletedOpenChannelToNpapiPlugin( + OpenChannelToNpapiPluginCallback* client) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + DCHECK(ContainsKey(plugin_host_clients_, client)); + plugin_host_clients_.erase(client); +} diff --git a/content/browser/renderer_host/render_message_filter.h b/content/browser/renderer_host/render_message_filter.h index 247571e..5d63975 100644 --- a/content/browser/renderer_host/render_message_filter.h +++ b/content/browser/renderer_host/render_message_filter.h @@ -76,6 +76,9 @@ class RenderMessageFilter : public BrowserMessageFilter { net::URLRequestContextGetter* request_context, RenderWidgetHelper* render_widget_helper); + // IPC::ChannelProxy::MessageFilter methods: + virtual void OnChannelClosing() OVERRIDE; + // BrowserMessageFilter methods: virtual void OverrideThreadForMessage(const IPC::Message& message, BrowserThread::ID* thread); @@ -99,6 +102,8 @@ class RenderMessageFilter : public BrowserMessageFilter { friend class BrowserThread; friend class DeleteTask<RenderMessageFilter>; + class OpenChannelToNpapiPluginCallback; + virtual ~RenderMessageFilter(); void OnMsgCreateWindow(const ViewHostMsg_CreateWindow_Params& params, @@ -226,6 +231,8 @@ class RenderMessageFilter : public BrowserMessageFilter { bool CheckBenchmarkingEnabled() const; bool CheckPreparsedJsCachingEnabled() const; + void OnCompletedOpenChannelToNpapiPlugin( + OpenChannelToNpapiPluginCallback* client); // Cached resource request dispatcher host and plugin service, guaranteed to // be non-null if Init succeeds. We do not own the objects, they are managed @@ -255,6 +262,8 @@ class RenderMessageFilter : public BrowserMessageFilter { int render_process_id_; + std::set<OpenChannelToNpapiPluginCallback*> plugin_host_clients_; + DISALLOW_COPY_AND_ASSIGN(RenderMessageFilter); }; |