summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--build/common.gypi2
-rw-r--r--content/browser/plugin_loader_posix.cc165
-rw-r--r--content/browser/plugin_loader_posix.h92
-rw-r--r--content/browser/plugin_loader_posix_unittest.cc278
-rw-r--r--content/browser/plugin_service.cc5
-rw-r--r--content/browser/plugin_service.h5
-rw-r--r--content/common/utility_messages.h22
-rw-r--r--content/content_tests.gypi1
-rw-r--r--content/utility/utility_thread_impl.cc38
-rw-r--r--content/utility/utility_thread_impl.h5
-rw-r--r--webkit/plugins/npapi/plugin_list.cc119
-rw-r--r--webkit/plugins/npapi/plugin_list.h26
-rw-r--r--webkit/plugins/npapi/plugin_list_mac.mm8
-rw-r--r--webkit/plugins/npapi/plugin_list_posix.cc10
-rw-r--r--webkit/plugins/npapi/plugin_list_win.cc15
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);
}
}