diff options
-rw-r--r-- | build/common.gypi | 2 | ||||
-rw-r--r-- | content/browser/plugin_loader_posix.cc | 165 | ||||
-rw-r--r-- | content/browser/plugin_loader_posix.h | 92 | ||||
-rw-r--r-- | content/browser/plugin_loader_posix_unittest.cc | 278 | ||||
-rw-r--r-- | content/browser/plugin_service.cc | 5 | ||||
-rw-r--r-- | content/browser/plugin_service.h | 5 | ||||
-rw-r--r-- | content/common/utility_messages.h | 22 | ||||
-rw-r--r-- | content/content_tests.gypi | 1 | ||||
-rw-r--r-- | content/utility/utility_thread_impl.cc | 38 | ||||
-rw-r--r-- | content/utility/utility_thread_impl.h | 5 | ||||
-rw-r--r-- | webkit/plugins/npapi/plugin_list.cc | 119 | ||||
-rw-r--r-- | webkit/plugins/npapi/plugin_list.h | 26 | ||||
-rw-r--r-- | webkit/plugins/npapi/plugin_list_mac.mm | 8 | ||||
-rw-r--r-- | webkit/plugins/npapi/plugin_list_posix.cc | 10 | ||||
-rw-r--r-- | webkit/plugins/npapi/plugin_list_win.cc | 15 |
15 files changed, 613 insertions, 178 deletions
diff --git a/build/common.gypi b/build/common.gypi index 917c33c..5537473 100644 --- a/build/common.gypi +++ b/build/common.gypi @@ -1252,7 +1252,7 @@ }], # We use "POSIX" to refer to all non-Windows operating systems. ['OS=="win"', { - 'sources/': [ ['exclude', '_posix\\.(h|cc)$'] ], + 'sources/': [ ['exclude', '_posix(_unittest)?\\.(h|cc)$'] ], # turn on warnings for signed/unsigned mismatch on chromium code. 'msvs_settings': { 'VCCLCompilerTool': { diff --git a/content/browser/plugin_loader_posix.cc b/content/browser/plugin_loader_posix.cc index da3d55a..e27e543 100644 --- a/content/browser/plugin_loader_posix.cc +++ b/content/browser/plugin_loader_posix.cc @@ -5,73 +5,162 @@ #include "content/browser/plugin_loader_posix.h" #include "base/bind.h" +#include "base/message_loop.h" #include "base/message_loop_proxy.h" +#include "base/metrics/histogram.h" #include "content/browser/browser_thread.h" #include "content/common/utility_messages.h" #include "webkit/plugins/npapi/plugin_list.h" -namespace { +using webkit::npapi::PluginList; -void RunGetPluginsCallback(const PluginService::GetPluginsCallback& callback, - const std::vector<webkit::WebPluginInfo> plugins) { - callback.Run(plugins); +PluginLoaderPosix::PluginLoaderPosix() + : next_load_index_(0) { } -} // namespace - -// static void PluginLoaderPosix::LoadPlugins( - base::MessageLoopProxy* target_loop, + scoped_refptr<base::MessageLoopProxy> target_loop, const PluginService::GetPluginsCallback& callback) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - PluginLoaderPosix* client = new PluginLoaderPosix(target_loop, callback); - UtilityProcessHost* process_host = - new UtilityProcessHost(client, BrowserThread::IO); - process_host->set_no_sandbox(true); -#if defined(OS_MACOSX) - process_host->set_child_flags(ChildProcessHost::CHILD_ALLOW_HEAP_EXECUTION); -#endif + callbacks_.push_back(PendingCallback(target_loop, callback)); - std::vector<FilePath> extra_plugin_paths; - std::vector<FilePath> extra_plugin_dirs; - std::vector<webkit::WebPluginInfo> internal_plugins; - webkit::npapi::PluginList::Singleton()->GetPluginPathListsToLoad( - &extra_plugin_paths, &extra_plugin_dirs, &internal_plugins); - - process_host->Send(new UtilityMsg_LoadPlugins( - extra_plugin_paths, extra_plugin_dirs, internal_plugins)); + if (callbacks_.size() == 1) { + BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, + base::Bind(&PluginLoaderPosix::GetPluginsToLoad, this)); + } } bool PluginLoaderPosix::OnMessageReceived(const IPC::Message& message) { bool handled = true; IPC_BEGIN_MESSAGE_MAP(PluginLoaderPosix, message) - IPC_MESSAGE_HANDLER(UtilityHostMsg_LoadedPlugins, OnGotPlugins) + IPC_MESSAGE_HANDLER(UtilityHostMsg_LoadedPlugin, OnPluginLoaded) + IPC_MESSAGE_HANDLER(UtilityHostMsg_LoadPluginFailed, OnPluginLoadFailed) IPC_MESSAGE_UNHANDLED(handled = false) IPC_END_MESSAGE_MAP() return handled; } void PluginLoaderPosix::OnProcessCrashed(int exit_code) { - LOG(ERROR) << "Out-of-process plugin loader crashed with code " << exit_code - << ". You will have no plugins!"; - // Don't leave callers hanging. - OnGotPlugins(std::vector<webkit::WebPluginInfo>()); + canonical_list_.erase(canonical_list_.begin(), + canonical_list_.begin() + next_load_index_ + 1); + next_load_index_ = 0; + LoadPluginsInternal(); } -PluginLoaderPosix::PluginLoaderPosix( - base::MessageLoopProxy* target_loop, - const PluginService::GetPluginsCallback& callback) - : target_loop_(target_loop), - callback_(callback) { +bool PluginLoaderPosix::Send(IPC::Message* message) { + return process_host_->Send(message); } PluginLoaderPosix::~PluginLoaderPosix() { } -void PluginLoaderPosix::OnGotPlugins( - const std::vector<webkit::WebPluginInfo>& plugins) { - webkit::npapi::PluginList::Singleton()->SetPlugins(plugins); - target_loop_->PostTask(FROM_HERE, - base::Bind(&RunGetPluginsCallback, callback_, plugins)); +void PluginLoaderPosix::GetPluginsToLoad() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + + base::TimeTicks start_time(base::TimeTicks::Now()); + + loaded_plugins_.clear(); + next_load_index_ = 0; + + canonical_list_.clear(); + webkit::npapi::PluginList::Singleton()->GetPluginPathsToLoad( + &canonical_list_); + + internal_plugins_.clear(); + PluginList::Singleton()->GetInternalPlugins(&internal_plugins_); + + BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, + base::Bind(&PluginLoaderPosix::LoadPluginsInternal, + make_scoped_refptr(this))); + + HISTOGRAM_TIMES("PluginLoaderPosix.GetPluginList", + (base::TimeTicks::Now() - start_time) * + base::Time::kMicrosecondsPerMillisecond); +} + +void PluginLoaderPosix::LoadPluginsInternal() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + + if (load_start_time_.is_null()) + load_start_time_ = base::TimeTicks::Now(); + + process_host_ = new UtilityProcessHost(this, BrowserThread::IO); + process_host_->set_no_sandbox(true); +#if defined(OS_MACOSX) + process_host_->set_child_flags(ChildProcessHost::CHILD_ALLOW_HEAP_EXECUTION); +#endif + + process_host_->Send(new UtilityMsg_LoadPlugins(canonical_list_)); +} + +void PluginLoaderPosix::OnPluginLoaded(const webkit::WebPluginInfo& plugin) { + if (plugin.path.value() != canonical_list_[next_load_index_].value()) { + LOG(ERROR) << "Received unexpected plugin load message for " + << plugin.path.value(); + return; + } + + if (!MaybeAddInternalPlugin(plugin.path)) + loaded_plugins_.push_back(plugin); + + ++next_load_index_; + + RunPendingCallbacks(); +} + +void PluginLoaderPosix::OnPluginLoadFailed(const FilePath& plugin_path) { + if (plugin_path.value() != canonical_list_[next_load_index_].value()) { + LOG(ERROR) << "Received unexpected plugin load failure message for " + << plugin_path.value(); + return; + } + + ++next_load_index_; + + MaybeAddInternalPlugin(plugin_path); + RunPendingCallbacks(); +} + +bool PluginLoaderPosix::MaybeAddInternalPlugin(const FilePath& plugin_path) { + for (std::vector<webkit::WebPluginInfo>::iterator it = + internal_plugins_.begin(); + it != internal_plugins_.end(); + ++it) { + if (it->path == plugin_path) { + loaded_plugins_.push_back(*it); + internal_plugins_.erase(it); + return true; + } + } + return false; +} + +void PluginLoaderPosix::RunPendingCallbacks() { + if (next_load_index_ < canonical_list_.size()) + return; + + PluginList::Singleton()->SetPlugins(loaded_plugins_); + for (std::vector<PendingCallback>::iterator it = callbacks_.begin(); + it != callbacks_.end(); + ++it) { + it->target_loop->PostTask(FROM_HERE, + base::Bind(it->callback, loaded_plugins_)); + } + callbacks_.clear(); + + HISTOGRAM_TIMES("PluginLoaderPosix.LoadDone", + (base::TimeTicks::Now() - load_start_time_) + * base::Time::kMicrosecondsPerMillisecond); + load_start_time_ = base::TimeTicks(); +} + +PluginLoaderPosix::PendingCallback::PendingCallback( + scoped_refptr<base::MessageLoopProxy> loop, + const PluginService::GetPluginsCallback& cb) + : target_loop(loop), + callback(cb) { +} + +PluginLoaderPosix::PendingCallback::~PendingCallback() { } diff --git a/content/browser/plugin_loader_posix.h b/content/browser/plugin_loader_posix.h index e766f9e..1af2957 100644 --- a/content/browser/plugin_loader_posix.h +++ b/content/browser/plugin_loader_posix.h @@ -8,36 +8,112 @@ #include <vector> #include "base/memory/ref_counted.h" +#include "base/time.h" #include "content/browser/plugin_service.h" #include "content/browser/utility_process_host.h" +#include "ipc/ipc_message.h" #include "webkit/plugins/webplugininfo.h" +class FilePath; +class UtilityProcessHost; + namespace base { class MessageLoopProxy; } -class PluginLoaderPosix : public UtilityProcessHost::Client { +// This class is responsible for managing the out-of-process plugin loading on +// POSIX systems. It primarily lives on the IO thread, but has a brief stay on +// the FILE thread to iterate over plugin directories when it is first +// constructed. +// +// The following is the algorithm used to load plugins: +// 1. This asks the PluginList for the list of all potential plugins to attempt +// to load. This is referred to as the canonical list. +// 2. The child process this hosts is forked and the canonical list is sent to +// it. +// 3. The child process iterates over the canonical list, attempting to load +// each plugin in the order specified by the list. It sends an IPC message +// to the browser after each load, indicating success or failure. The two +// processes synchronize the position in the vector that will be used to +// attempt to load the next plugin. +// 4. If the child dies during this process, the host forks another child and +// resumes loading at the position past the plugin that it just attempted to +// load, bypassing the problematic plugin. +// 5. This algorithm continues until the canonical list has been walked to the +// end, after which the list of loaded plugins is set on the PluginList and +// the completion callback is run. +class PluginLoaderPosix : public UtilityProcessHost::Client, + IPC::Message::Sender { public: - // Must be called on the IO thread. - static void LoadPlugins(base::MessageLoopProxy* target_loop, - const PluginService::GetPluginsCallback& callback); + PluginLoaderPosix(); + + // Must be called from the IO thread. + void LoadPlugins(scoped_refptr<base::MessageLoopProxy> target_loop, + const PluginService::GetPluginsCallback& callback); // UtilityProcessHost::Client: virtual void OnProcessCrashed(int exit_code) OVERRIDE; virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE; + // IPC::Message::Sender: + virtual bool Send(IPC::Message* msg); + private: - PluginLoaderPosix(base::MessageLoopProxy* target_loop, + struct PendingCallback { + PendingCallback(scoped_refptr<base::MessageLoopProxy> target_loop, const PluginService::GetPluginsCallback& callback); + ~PendingCallback(); + + scoped_refptr<base::MessageLoopProxy> target_loop; + PluginService::GetPluginsCallback callback; + }; + virtual ~PluginLoaderPosix(); - void OnGotPlugins(const std::vector<webkit::WebPluginInfo>& plugins); + // Called on the FILE thread to get the list of plugin paths to probe. + void GetPluginsToLoad(); + + // Must be called on the IO thread. + virtual void LoadPluginsInternal(); + + // Message handlers. + void OnPluginLoaded(const webkit::WebPluginInfo& plugin); + void OnPluginLoadFailed(const FilePath& plugin_path); + + // Checks if the plugin path is an internal plugin, and, if it is, adds it to + // |loaded_plugins_|. + bool MaybeAddInternalPlugin(const FilePath& plugin_path); + + // Runs all the registered callbacks on each's target loop if the condition + // for ending the load process is done (i.e. the |next_load_index_| is outside + // the ranage of the |canonical_list_|). + void RunPendingCallbacks(); + + // The process host for which this is a client. + UtilityProcessHost* process_host_; + + // A list of paths to plugins which will be loaded by the utility process, in + // the order specified by this vector. + std::vector<FilePath> canonical_list_; + + // The index in |canonical_list_| of the plugin that the child process will + // attempt to load next. + size_t next_load_index_; + + // Internal plugins that have been registered at the time of loading. + std::vector<webkit::WebPluginInfo> internal_plugins_; + + // A vector of plugins that have been loaded successfully. + std::vector<webkit::WebPluginInfo> loaded_plugins_; // The callback and message loop on which the callback will be run when the // plugin loading process has been completed. - scoped_refptr<base::MessageLoopProxy> target_loop_; - PluginService::GetPluginsCallback callback_; + std::vector<PendingCallback> callbacks_; + + // The time at which plugin loading started. + base::TimeTicks load_start_time_; + friend class MockPluginLoaderPosix; DISALLOW_COPY_AND_ASSIGN(PluginLoaderPosix); }; diff --git a/content/browser/plugin_loader_posix_unittest.cc b/content/browser/plugin_loader_posix_unittest.cc new file mode 100644 index 0000000..eb18428 --- /dev/null +++ b/content/browser/plugin_loader_posix_unittest.cc @@ -0,0 +1,278 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/browser/plugin_loader_posix.h" + +#include "base/bind.h" +#include "base/file_path.h" +#include "base/message_loop.h" +#include "base/memory/ref_counted.h" +#include "base/utf_string_conversions.h" +#include "content/browser/browser_thread.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "webkit/plugins/webplugininfo.h" + +class MockPluginLoaderPosix : public PluginLoaderPosix { + public: + MOCK_METHOD0(LoadPluginsInternal, void(void)); + + size_t number_of_pending_callbacks() { + return callbacks_.size(); + } + + std::vector<FilePath>* canonical_list() { + return &canonical_list_; + } + + size_t next_load_index() { + return next_load_index_; + } + + const std::vector<webkit::WebPluginInfo>& loaded_plugins() { + return loaded_plugins_; + } + + std::vector<webkit::WebPluginInfo>* internal_plugins() { + return &internal_plugins_; + } + + void TestOnPluginLoaded(const webkit::WebPluginInfo& plugin) { + OnPluginLoaded(plugin); + } + + void TestOnPluginLoadFailed(const FilePath& path) { + OnPluginLoadFailed(path); + } +}; + +void VerifyCallback(int* run_count, const std::vector<webkit::WebPluginInfo>&) { + ++(*run_count); +} + +class PluginLoaderPosixTest : public testing::Test { + public: + PluginLoaderPosixTest() + : plugin1_(ASCIIToUTF16("plugin1"), FilePath("/tmp/one.plugin"), + ASCIIToUTF16("1.0"), string16()), + plugin2_(ASCIIToUTF16("plugin2"), FilePath("/tmp/two.plugin"), + ASCIIToUTF16("2.0"), string16()), + plugin3_(ASCIIToUTF16("plugin3"), FilePath("/tmp/three.plugin"), + ASCIIToUTF16("3.0"), string16()), + file_thread_(BrowserThread::FILE, &message_loop_), + io_thread_(BrowserThread::IO, &message_loop_), + plugin_loader_(new MockPluginLoaderPosix) { + } + + MessageLoop* message_loop() { return &message_loop_; } + MockPluginLoaderPosix* plugin_loader() { return plugin_loader_.get(); } + + void AddThreePlugins() { + plugin_loader_->canonical_list()->clear(); + plugin_loader_->canonical_list()->push_back(plugin1_.path); + plugin_loader_->canonical_list()->push_back(plugin2_.path); + plugin_loader_->canonical_list()->push_back(plugin3_.path); + } + + // Data used for testing. + webkit::WebPluginInfo plugin1_; + webkit::WebPluginInfo plugin2_; + webkit::WebPluginInfo plugin3_; + + private: + MessageLoopForIO message_loop_; + BrowserThread file_thread_; + BrowserThread io_thread_; + + scoped_refptr<MockPluginLoaderPosix> plugin_loader_; +}; + +TEST_F(PluginLoaderPosixTest, QueueRequests) { + int did_callback = 0; + PluginService::GetPluginsCallback callback = + base::Bind(&VerifyCallback, base::Unretained(&did_callback)); + + EXPECT_EQ(0u, plugin_loader()->number_of_pending_callbacks()); + plugin_loader()->LoadPlugins(message_loop()->message_loop_proxy(), callback); + EXPECT_EQ(1u, plugin_loader()->number_of_pending_callbacks()); + + plugin_loader()->LoadPlugins(message_loop()->message_loop_proxy(), callback); + EXPECT_EQ(2u, plugin_loader()->number_of_pending_callbacks()); + + EXPECT_CALL(*plugin_loader(), LoadPluginsInternal()).Times(1); + message_loop()->RunAllPending(); + + plugin_loader()->canonical_list()->clear(); + plugin_loader()->canonical_list()->push_back(plugin1_.path); + + EXPECT_EQ(0, did_callback); + + plugin_loader()->TestOnPluginLoaded(plugin1_); + message_loop()->RunAllPending(); + + EXPECT_EQ(2, did_callback); + EXPECT_EQ(0u, plugin_loader()->number_of_pending_callbacks()); +} + +TEST_F(PluginLoaderPosixTest, ThreeSuccessfulLoads) { + int did_callback = 0; + PluginService::GetPluginsCallback callback = + base::Bind(&VerifyCallback, base::Unretained(&did_callback)); + + plugin_loader()->LoadPlugins(message_loop()->message_loop_proxy(), callback); + + EXPECT_CALL(*plugin_loader(), LoadPluginsInternal()).Times(1); + message_loop()->RunAllPending(); + + AddThreePlugins(); + + EXPECT_EQ(0u, plugin_loader()->next_load_index()); + + const std::vector<webkit::WebPluginInfo>& plugins( + plugin_loader()->loaded_plugins()); + + plugin_loader()->TestOnPluginLoaded(plugin1_); + EXPECT_EQ(1u, plugin_loader()->next_load_index()); + EXPECT_EQ(1u, plugins.size()); + EXPECT_EQ(plugin1_.name, plugins[0].name); + + message_loop()->RunAllPending(); + EXPECT_EQ(0, did_callback); + + plugin_loader()->TestOnPluginLoaded(plugin2_); + EXPECT_EQ(2u, plugin_loader()->next_load_index()); + EXPECT_EQ(2u, plugins.size()); + EXPECT_EQ(plugin2_.name, plugins[1].name); + + message_loop()->RunAllPending(); + EXPECT_EQ(0, did_callback); + + plugin_loader()->TestOnPluginLoaded(plugin3_); + EXPECT_EQ(3u, plugins.size()); + EXPECT_EQ(plugin3_.name, plugins[2].name); + + message_loop()->RunAllPending(); + EXPECT_EQ(1, did_callback); +} + +TEST_F(PluginLoaderPosixTest, TwoFailures) { + int did_callback = 0; + PluginService::GetPluginsCallback callback = + base::Bind(&VerifyCallback, base::Unretained(&did_callback)); + + plugin_loader()->LoadPlugins(message_loop()->message_loop_proxy(), callback); + + EXPECT_CALL(*plugin_loader(), LoadPluginsInternal()).Times(1); + message_loop()->RunAllPending(); + + AddThreePlugins(); + + EXPECT_EQ(0u, plugin_loader()->next_load_index()); + + const std::vector<webkit::WebPluginInfo>& plugins( + plugin_loader()->loaded_plugins()); + + plugin_loader()->TestOnPluginLoadFailed(plugin1_.path); + EXPECT_EQ(1u, plugin_loader()->next_load_index()); + EXPECT_EQ(0u, plugins.size()); + + message_loop()->RunAllPending(); + EXPECT_EQ(0, did_callback); + + plugin_loader()->TestOnPluginLoaded(plugin2_); + EXPECT_EQ(2u, plugin_loader()->next_load_index()); + EXPECT_EQ(1u, plugins.size()); + EXPECT_EQ(plugin2_.name, plugins[0].name); + + message_loop()->RunAllPending(); + EXPECT_EQ(0, did_callback); + + plugin_loader()->TestOnPluginLoadFailed(plugin3_.path); + EXPECT_EQ(1u, plugins.size()); + + message_loop()->RunAllPending(); + EXPECT_EQ(1, did_callback); +} + +TEST_F(PluginLoaderPosixTest, CrashedProcess) { + int did_callback = 0; + PluginService::GetPluginsCallback callback = + base::Bind(&VerifyCallback, base::Unretained(&did_callback)); + + plugin_loader()->LoadPlugins(message_loop()->message_loop_proxy(), callback); + + EXPECT_CALL(*plugin_loader(), LoadPluginsInternal()).Times(1); + message_loop()->RunAllPending(); + + AddThreePlugins(); + + EXPECT_EQ(0u, plugin_loader()->next_load_index()); + + const std::vector<webkit::WebPluginInfo>& plugins( + plugin_loader()->loaded_plugins()); + + plugin_loader()->TestOnPluginLoaded(plugin1_); + EXPECT_EQ(1u, plugin_loader()->next_load_index()); + EXPECT_EQ(1u, plugins.size()); + EXPECT_EQ(plugin1_.name, plugins[0].name); + + message_loop()->RunAllPending(); + EXPECT_EQ(0, did_callback); + + EXPECT_CALL(*plugin_loader(), LoadPluginsInternal()).Times(1); + plugin_loader()->OnProcessCrashed(42); + EXPECT_EQ(1u, plugin_loader()->canonical_list()->size()); + EXPECT_EQ(0u, plugin_loader()->next_load_index()); + EXPECT_EQ(plugin3_.path.value(), + plugin_loader()->canonical_list()->at(0).value()); +} + +TEST_F(PluginLoaderPosixTest, InternalPlugin) { + int did_callback = 0; + PluginService::GetPluginsCallback callback = + base::Bind(&VerifyCallback, base::Unretained(&did_callback)); + + plugin_loader()->LoadPlugins(message_loop()->message_loop_proxy(), callback); + + EXPECT_CALL(*plugin_loader(), LoadPluginsInternal()).Times(1); + message_loop()->RunAllPending(); + + plugin2_.path = FilePath("/internal/plugin.plugin"); + + AddThreePlugins(); + + plugin_loader()->internal_plugins()->clear(); + plugin_loader()->internal_plugins()->push_back(plugin2_); + + EXPECT_EQ(0u, plugin_loader()->next_load_index()); + + const std::vector<webkit::WebPluginInfo>& plugins( + plugin_loader()->loaded_plugins()); + + plugin_loader()->TestOnPluginLoaded(plugin1_); + EXPECT_EQ(1u, plugin_loader()->next_load_index()); + EXPECT_EQ(1u, plugins.size()); + EXPECT_EQ(plugin1_.name, plugins[0].name); + + message_loop()->RunAllPending(); + EXPECT_EQ(0, did_callback); + + // Internal plugins can fail to load if they're built-in with manual + // entrypoint functions. + plugin_loader()->TestOnPluginLoadFailed(plugin2_.path); + EXPECT_EQ(2u, plugin_loader()->next_load_index()); + EXPECT_EQ(2u, plugins.size()); + EXPECT_EQ(plugin2_.name, plugins[1].name); + EXPECT_EQ(0u, plugin_loader()->internal_plugins()->size()); + + message_loop()->RunAllPending(); + EXPECT_EQ(0, did_callback); + + plugin_loader()->TestOnPluginLoaded(plugin3_); + EXPECT_EQ(3u, plugins.size()); + EXPECT_EQ(plugin3_.name, plugins[2].name); + + message_loop()->RunAllPending(); + EXPECT_EQ(1, did_callback); +} diff --git a/content/browser/plugin_service.cc b/content/browser/plugin_service.cc index c189c31..7eee9a4 100644 --- a/content/browser/plugin_service.cc +++ b/content/browser/plugin_service.cc @@ -508,8 +508,11 @@ void PluginService::GetPlugins(const GetPluginsCallback& callback) { target_loop->PostTask(FROM_HERE, base::Bind(&RunGetPluginsCallback, callback, cached_plugins)); } else { + if (!plugin_loader_.get()) + plugin_loader_ = new PluginLoaderPosix; BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, - base::Bind(&PluginLoaderPosix::LoadPlugins, target_loop, callback)); + base::Bind(&PluginLoaderPosix::LoadPlugins, plugin_loader_, + target_loop, callback)); } #endif } diff --git a/content/browser/plugin_service.h b/content/browser/plugin_service.h index 224d243..2b2c69f 100644 --- a/content/browser/plugin_service.h +++ b/content/browser/plugin_service.h @@ -36,6 +36,7 @@ #endif class PluginDirWatcherDelegate; +class PluginLoaderPosix; namespace base { class MessageLoopProxy; @@ -258,6 +259,10 @@ class CONTENT_EXPORT PluginService std::set<PluginProcessHost::Client*> pending_plugin_clients_; +#if defined(OS_POSIX) + scoped_refptr<PluginLoaderPosix> plugin_loader_; +#endif + DISALLOW_COPY_AND_ASSIGN(PluginService); }; diff --git a/content/common/utility_messages.h b/content/common/utility_messages.h index 6b54221..2cee196 100644 --- a/content/common/utility_messages.h +++ b/content/common/utility_messages.h @@ -40,12 +40,10 @@ IPC_MESSAGE_CONTROL0(UtilityMsg_BatchMode_Started) IPC_MESSAGE_CONTROL0(UtilityMsg_BatchMode_Finished) #if defined(OS_POSIX) -// Tells the utility process to load the plugins from disk and send a list of -// WebPluginInfo objects back. -IPC_MESSAGE_CONTROL3(UtilityMsg_LoadPlugins, - std::vector<FilePath>, /* extra plugin paths */ - std::vector<FilePath>, /* extra plugin dirs */ - std::vector<webkit::WebPluginInfo> /* internal plugins */) +// Tells the utility process to load each plugin in the order specified by the +// vector. It will respond after each load with the WebPluginInfo. +IPC_MESSAGE_CONTROL1(UtilityMsg_LoadPlugins, + std::vector<FilePath> /* plugin paths */) #endif //------------------------------------------------------------------------------ @@ -69,8 +67,12 @@ IPC_MESSAGE_CONTROL1(UtilityHostMsg_InjectIDBKey_Finished, SerializedScriptValue /* new value */) #if defined(OS_POSIX) -// After loading plugins from disk and querying each for MIME information, this -// sends the resulting WebPluginInfo back to the browser process. -IPC_MESSAGE_CONTROL1(UtilityHostMsg_LoadedPlugins, - std::vector<webkit::WebPluginInfo> /* plugin infos */) +// Notifies the browser when a plugin failed to load so the two processes can +// keep the canonical list in sync. +IPC_SYNC_MESSAGE_CONTROL1_0(UtilityHostMsg_LoadPluginFailed, + FilePath /* path of plugin */) + +// Notifies the browser that a plugin in the vector sent by it has been loaded. +IPC_SYNC_MESSAGE_CONTROL1_0(UtilityHostMsg_LoadedPlugin, + webkit::WebPluginInfo /* plugin info */) #endif // OS_POSIX diff --git a/content/content_tests.gypi b/content/content_tests.gypi index a7549a8..bba6e9d 100644 --- a/content/content_tests.gypi +++ b/content/content_tests.gypi @@ -155,6 +155,7 @@ 'browser/mac/closure_blocks_leopard_compat_unittest.cc', 'browser/mach_broker_mac_unittest.cc', 'browser/notification_service_impl_unittest.cc', + 'browser/plugin_loader_posix_unittest.cc', 'browser/renderer_host/accelerated_plugin_view_mac_unittest.mm', 'browser/renderer_host/gtk_key_bindings_handler_unittest.cc', 'browser/renderer_host/media/audio_input_device_manager_unittest.cc', diff --git a/content/utility/utility_thread_impl.cc b/content/utility/utility_thread_impl.cc index ca1a7e3..393dc3b 100644 --- a/content/utility/utility_thread_impl.cc +++ b/content/utility/utility_thread_impl.cc @@ -7,6 +7,7 @@ #include <stddef.h> #include "base/file_path.h" +#include "base/memory/scoped_vector.h" #include "content/common/child_process.h" #include "content/common/indexed_db_key.h" #include "content/common/utility_messages.h" @@ -107,34 +108,27 @@ void UtilityThreadImpl::OnBatchModeFinished() { #if defined(OS_POSIX) void UtilityThreadImpl::OnLoadPlugins( - const std::vector<FilePath>& extra_plugin_paths, - const std::vector<FilePath>& extra_plugin_dirs, - const std::vector<webkit::WebPluginInfo>& internal_plugins) { + const std::vector<FilePath>& plugin_paths) { webkit::npapi::PluginList* plugin_list = webkit::npapi::PluginList::Singleton(); - // Create the PluginList and set the paths from which to load plugins. Iterate - // in reverse to preserve the order when pushing back. - std::vector<FilePath>::const_reverse_iterator it; - for (it = extra_plugin_paths.rbegin(); - it != extra_plugin_paths.rend(); + for (std::vector<FilePath>::const_iterator it = plugin_paths.begin(); + it != plugin_paths.end(); ++it) { - plugin_list->AddExtraPluginPath(*it); - } - for (it = extra_plugin_dirs.rbegin(); it != extra_plugin_dirs.rend(); ++it) { - plugin_list->AddExtraPluginDir(*it); - } - for (std::vector<webkit::WebPluginInfo>::const_reverse_iterator it = - internal_plugins.rbegin(); - it != internal_plugins.rend(); - ++it) { - plugin_list->RegisterInternalPlugin(*it); - } + ScopedVector<webkit::npapi::PluginGroup> plugin_groups; + plugin_list->LoadPlugin(*it, &plugin_groups); - std::vector<webkit::WebPluginInfo> plugins; - plugin_list->GetPlugins(&plugins); + if (plugin_groups.empty()) { + Send(new UtilityHostMsg_LoadPluginFailed(*it)); + continue; + } + + const webkit::npapi::PluginGroup* group = plugin_groups[0]; + DCHECK_EQ(group->web_plugin_infos().size(), 1u); + + Send(new UtilityHostMsg_LoadedPlugin(group->web_plugin_infos().front())); + } - Send(new UtilityHostMsg_LoadedPlugins(plugins)); ReleaseProcessIfNeeded(); } #endif diff --git a/content/utility/utility_thread_impl.h b/content/utility/utility_thread_impl.h index 3af6c32..d7352d2 100644 --- a/content/utility/utility_thread_impl.h +++ b/content/utility/utility_thread_impl.h @@ -55,10 +55,7 @@ class UtilityThreadImpl : public content::UtilityThread, void OnBatchModeFinished(); #if defined(OS_POSIX) - void OnLoadPlugins( - const std::vector<FilePath>& extra_plugin_paths, - const std::vector<FilePath>& extra_plugin_dirs, - const std::vector<webkit::WebPluginInfo>& internal_plugins); + void OnLoadPlugins(const std::vector<FilePath>& plugin_paths); #endif // OS_POSIX // True when we're running in batch mode. diff --git a/webkit/plugins/npapi/plugin_list.cc b/webkit/plugins/npapi/plugin_list.cc index 9bad351..a59ff5c 100644 --- a/webkit/plugins/npapi/plugin_list.cc +++ b/webkit/plugins/npapi/plugin_list.cc @@ -257,6 +257,17 @@ void PluginList::UnregisterInternalPlugin(const FilePath& path) { NOTREACHED(); } +void PluginList::GetInternalPlugins( + std::vector<webkit::WebPluginInfo>* internal_plugins) { + base::AutoLock lock(lock_); + + for (std::vector<InternalPlugin>::iterator it = internal_plugins_.begin(); + it != internal_plugins_.end(); + ++it) { + internal_plugins->push_back(it->info); + } +} + bool PluginList::ReadPluginInfo(const FilePath& filename, webkit::WebPluginInfo* info, const PluginEntryPoints** entry_points) { @@ -348,11 +359,6 @@ PluginGroup* PluginList::CreatePluginGroup( } void PluginList::LoadPluginsInternal(ScopedVector<PluginGroup>* plugin_groups) { - // Don't want to hold the lock while loading new plugins, so we don't block - // other methods if they're called on other threads. - std::vector<FilePath> extra_plugin_paths; - std::vector<FilePath> extra_plugin_dirs; - std::vector<InternalPlugin> internal_plugins; base::Closure will_load_callback; { base::AutoLock lock(lock_); @@ -360,53 +366,19 @@ void PluginList::LoadPluginsInternal(ScopedVector<PluginGroup>* plugin_groups) { // reach the end of the method. plugins_need_refresh_ = false; will_load_callback = will_load_plugins_callback_; - extra_plugin_paths = extra_plugin_paths_; - extra_plugin_dirs = extra_plugin_dirs_; - internal_plugins = internal_plugins_; } if (!will_load_callback.is_null()) will_load_callback.Run(); - std::set<FilePath> visited_plugins; - - std::vector<FilePath> directories_to_scan; - GetPluginDirectories(&directories_to_scan); - - // Load internal plugins first so that, if both an internal plugin and a - // "discovered" plugin want to handle the same type, the internal plugin - // will have precedence. - for (size_t i = 0; i < internal_plugins.size(); ++i) { - if (internal_plugins[i].info.path.value() == kDefaultPluginLibraryName) - continue; - LoadPlugin(internal_plugins[i].info.path, plugin_groups); - } + std::vector<FilePath> plugin_paths; + GetPluginPathsToLoad(&plugin_paths); - for (size_t i = 0; i < extra_plugin_paths.size(); ++i) { - const FilePath& path = extra_plugin_paths[i]; - if (visited_plugins.find(path) != visited_plugins.end()) - continue; - LoadPlugin(path, plugin_groups); - visited_plugins.insert(path); - } - - for (size_t i = 0; i < extra_plugin_dirs.size(); ++i) { - LoadPluginsFromDir( - extra_plugin_dirs[i], plugin_groups, &visited_plugins); - } - - for (size_t i = 0; i < directories_to_scan.size(); ++i) { - LoadPluginsFromDir( - directories_to_scan[i], plugin_groups, &visited_plugins); + for (std::vector<FilePath>::const_iterator it = plugin_paths.begin(); + it != plugin_paths.end(); + ++it) { + LoadPlugin(*it, plugin_groups); } - -#if defined(OS_WIN) - LoadPluginsFromRegistry(plugin_groups, &visited_plugins); -#endif - - // Load the default plugin last. - if (default_plugin_enabled_) - LoadPlugin(FilePath(kDefaultPluginLibraryName), plugin_groups); } void PluginList::LoadPlugins() { @@ -456,20 +428,53 @@ void PluginList::LoadPlugin(const FilePath& path, AddToPluginGroups(plugin_info, plugin_groups); } -void PluginList::GetPluginPathListsToLoad( - std::vector<FilePath>* extra_plugin_paths, - std::vector<FilePath>* extra_plugin_dirs, - std::vector<webkit::WebPluginInfo>* internal_plugins) { - base::AutoLock lock(lock_); - *extra_plugin_paths = extra_plugin_paths_; - *extra_plugin_dirs = extra_plugin_dirs_; +void PluginList::GetPluginPathsToLoad(std::vector<FilePath>* plugin_paths) { + // Don't want to hold the lock while loading new plugins, so we don't block + // other methods if they're called on other threads. + std::vector<FilePath> extra_plugin_paths; + std::vector<FilePath> extra_plugin_dirs; + std::vector<InternalPlugin> internal_plugins; + { + base::AutoLock lock(lock_); + extra_plugin_paths = extra_plugin_paths_; + extra_plugin_dirs = extra_plugin_dirs_; + internal_plugins = internal_plugins_; + } - *internal_plugins = std::vector<webkit::WebPluginInfo>(); - for (std::vector<InternalPlugin>::iterator it = internal_plugins_.begin(); - it != internal_plugins_.end(); - ++it) { - internal_plugins->push_back(it->info); + std::vector<FilePath> directories_to_scan; + GetPluginDirectories(&directories_to_scan); + + // Load internal plugins first so that, if both an internal plugin and a + // "discovered" plugin want to handle the same type, the internal plugin + // will have precedence. + for (size_t i = 0; i < internal_plugins.size(); ++i) { + if (internal_plugins[i].info.path.value() == kDefaultPluginLibraryName) + continue; + plugin_paths->push_back(internal_plugins[i].info.path); } + + for (size_t i = 0; i < extra_plugin_paths.size(); ++i) { + const FilePath& path = extra_plugin_paths[i]; + if (std::find(plugin_paths->begin(), plugin_paths->end(), path) != + plugin_paths->end()) { + continue; + } + plugin_paths->push_back(path); + } + + for (size_t i = 0; i < extra_plugin_dirs.size(); ++i) + GetPluginsInDir(extra_plugin_dirs[i], plugin_paths); + + for (size_t i = 0; i < directories_to_scan.size(); ++i) + GetPluginsInDir(directories_to_scan[i], plugin_paths); + +#if defined(OS_WIN) + GetPluginPathsFromRegistry(plugin_paths); +#endif + + // Load the default plugin last. + if (default_plugin_enabled_) + plugin_paths->push_back(FilePath(kDefaultPluginLibraryName)); } void PluginList::SetPlugins(const std::vector<webkit::WebPluginInfo>& plugins) { diff --git a/webkit/plugins/npapi/plugin_list.h b/webkit/plugins/npapi/plugin_list.h index c8fa5ff..1669251 100644 --- a/webkit/plugins/npapi/plugin_list.h +++ b/webkit/plugins/npapi/plugin_list.h @@ -102,6 +102,9 @@ class PluginList { // This is generally only necessary for tests. void UnregisterInternalPlugin(const FilePath& path); + // Gets a list of all the registered internal plugins. + void GetInternalPlugins(std::vector<webkit::WebPluginInfo>* plugins); + // Creates a WebPluginInfo structure given a plugin's path. On success // returns true, with the information being put into "info". If it's an // internal plugin, "entry_points" is filled in as well with a @@ -169,12 +172,8 @@ class PluginList { // The following functions are used to support probing for WebPluginInfo // using a different instance of this class. - // Returns the extra plugin paths, extra plugin directories, and internal - // plugin paths that should be loaded. - void GetPluginPathListsToLoad( - std::vector<FilePath>* extra_plugin_paths, - std::vector<FilePath>* extra_plugin_dirs, - std::vector<webkit::WebPluginInfo>* internal_plugins); + // Computes a list of all plugins to potentially load from all sources. + void GetPluginPathsToLoad(std::vector<FilePath>* plugin_paths); // Clears the internal list of PluginGroups and copies them from the vector. void SetPlugins(const std::vector<webkit::WebPluginInfo>& plugins); @@ -220,13 +219,9 @@ class PluginList { // Load all plugins from the default plugins directory. void LoadPlugins(); - // Load all plugins from a specific directory. - // |plugin_groups| is updated with loaded plugin information. - // |visited_plugins| is updated with paths to all plugins that were considered - // (including those we didn't load). - void LoadPluginsFromDir(const FilePath& path, - ScopedVector<PluginGroup>* plugin_groups, - std::set<FilePath>* visited_plugins); + // Walks a directory and produces a list of all the plugins to potentially + // load in that directory. + void GetPluginsInDir(const FilePath& path, std::vector<FilePath>* plugins); // Returns true if we should load the given plugin, or false otherwise. // |plugins| is the list of plugins we have crawled in the current plugin @@ -263,10 +258,9 @@ class PluginList { // true if we shouldn't load the new WMP plugin. bool dont_load_new_wmp_; - // Loads plugins registered under HKCU\Software\MozillaPlugins and + // Gets plugin paths registered under HKCU\Software\MozillaPlugins and // HKLM\Software\MozillaPlugins. - void LoadPluginsFromRegistry(ScopedVector<PluginGroup>* plugins, - std::set<FilePath>* visited_plugins); + void GetPluginPathsFromRegistry(std::vector<FilePath>* plugins); #endif // diff --git a/webkit/plugins/npapi/plugin_list_mac.mm b/webkit/plugins/npapi/plugin_list_mac.mm index 361691a..62c3768a 100644 --- a/webkit/plugins/npapi/plugin_list_mac.mm +++ b/webkit/plugins/npapi/plugin_list_mac.mm @@ -84,16 +84,14 @@ void PluginList::GetPluginDirectories(std::vector<FilePath>* plugin_dirs) { } } -void PluginList::LoadPluginsFromDir(const FilePath &path, - ScopedVector<PluginGroup>* plugin_groups, - std::set<FilePath>* visited_plugins) { +void PluginList::GetPluginsInDir( + const FilePath& path, std::vector<FilePath>* plugins) { file_util::FileEnumerator enumerator(path, false, // not recursive file_util::FileEnumerator::DIRECTORIES); for (FilePath path = enumerator.Next(); !path.value().empty(); path = enumerator.Next()) { - LoadPlugin(path, plugin_groups); - visited_plugins->insert(path); + plugins->push_back(path); } } diff --git a/webkit/plugins/npapi/plugin_list_posix.cc b/webkit/plugins/npapi/plugin_list_posix.cc index 7402aef..a7f8082 100644 --- a/webkit/plugins/npapi/plugin_list_posix.cc +++ b/webkit/plugins/npapi/plugin_list_posix.cc @@ -164,9 +164,8 @@ void PluginList::GetPluginDirectories(std::vector<FilePath>* plugin_dirs) { #endif // !defined(OS_CHROMEOS) } -void PluginList::LoadPluginsFromDir(const FilePath& dir_path, - ScopedVector<PluginGroup>* plugin_groups, - std::set<FilePath>* visited_plugins) { +void PluginList::GetPluginsInDir( + const FilePath& dir_path, std::vector<FilePath>* plugins) { // See ScanPluginsDirectory near // http://mxr.mozilla.org/firefox/source/modules/plugin/base/src/nsPluginHostImpl.cpp#5052 @@ -191,12 +190,11 @@ void PluginList::LoadPluginsFromDir(const FilePath& dir_path, LOG_IF(ERROR, PluginList::DebugPluginLoading()) << "Resolved " << orig_path.value() << " -> " << path.value(); - if (visited_plugins->find(path) != visited_plugins->end()) { + if (std::find(plugins->begin(), plugins->end(), path) != plugins->end()) { LOG_IF(ERROR, PluginList::DebugPluginLoading()) << "Skipping duplicate instance of " << path.value(); continue; } - visited_plugins->insert(path); if (IsBlacklistedPlugin(path)) { LOG_IF(ERROR, PluginList::DebugPluginLoading()) @@ -234,7 +232,7 @@ void PluginList::LoadPluginsFromDir(const FilePath& dir_path, // Load the files in order. for (FileTimeList::const_iterator i = files.begin(); i != files.end(); ++i) { - LoadPlugin(i->first, plugin_groups); + plugins->push_back(i->first); } } diff --git a/webkit/plugins/npapi/plugin_list_win.cc b/webkit/plugins/npapi/plugin_list_win.cc index 0f832e9..c0b2bef 100644 --- a/webkit/plugins/npapi/plugin_list_win.cc +++ b/webkit/plugins/npapi/plugin_list_win.cc @@ -267,9 +267,8 @@ void PluginList::GetPluginDirectories(std::vector<FilePath>* plugin_dirs) { plugin_dirs->push_back(*i); } -void PluginList::LoadPluginsFromDir(const FilePath &path, - ScopedVector<PluginGroup>* plugin_groups, - std::set<FilePath>* visited_plugins) { +void PluginList::GetPluginsInDir( + const FilePath& path, std::vector<FilePath>* plugins) { WIN32_FIND_DATA find_file_data; HANDLE find_handle; @@ -284,8 +283,7 @@ void PluginList::LoadPluginsFromDir(const FilePath &path, do { if (!(find_file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { FilePath filename = path.Append(find_file_data.cFileName); - LoadPlugin(filename, plugin_groups); - visited_plugins->insert(filename); + plugins->push_back(filename); } } while (FindNextFile(find_handle, &find_file_data) != 0); @@ -293,9 +291,7 @@ void PluginList::LoadPluginsFromDir(const FilePath &path, FindClose(find_handle); } -void PluginList::LoadPluginsFromRegistry( - ScopedVector<PluginGroup>* plugin_groups, - std::set<FilePath>* visited_plugins) { +void PluginList::GetPluginPathsFromRegistry(std::vector<FilePath>* plugins) { std::set<FilePath> plugin_dirs; GetPluginsInRegistryDirectory( @@ -305,8 +301,7 @@ void PluginList::LoadPluginsFromRegistry( for (std::set<FilePath>::iterator i = plugin_dirs.begin(); i != plugin_dirs.end(); ++i) { - LoadPlugin(*i, plugin_groups); - visited_plugins->insert(*i); + plugins->push_back(*i); } } |