Re-subimission of https://codereview.chromium.org/1041213003/
[chromium-blink-merge.git] / content / browser / plugin_loader_posix.cc
blobb8750ed90ea3cf037c36132856c1dc1c8cd9f537
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "content/browser/plugin_loader_posix.h"
7 #include "base/bind.h"
8 #include "base/message_loop/message_loop.h"
9 #include "base/message_loop/message_loop_proxy.h"
10 #include "base/metrics/histogram.h"
11 #include "content/browser/utility_process_host_impl.h"
12 #include "content/common/child_process_host_impl.h"
13 #include "content/common/plugin_list.h"
14 #include "content/common/utility_messages.h"
15 #include "content/public/browser/browser_thread.h"
16 #include "content/public/browser/plugin_service.h"
17 #include "content/public/browser/user_metrics.h"
19 namespace content {
21 PluginLoaderPosix::PluginLoaderPosix()
22 : next_load_index_(0), loading_plugins_(false) {
25 void PluginLoaderPosix::GetPlugins(
26 const PluginService::GetPluginsCallback& callback) {
27 DCHECK_CURRENTLY_ON(BrowserThread::IO);
29 std::vector<WebPluginInfo> cached_plugins;
30 if (PluginList::Singleton()->GetPluginsNoRefresh(&cached_plugins)) {
31 // Can't assume the caller is reentrant.
32 base::MessageLoop::current()->PostTask(FROM_HERE,
33 base::Bind(callback, cached_plugins));
34 return;
37 if (!loading_plugins_) {
38 loading_plugins_ = true;
39 callbacks_.push_back(callback);
41 // When |loading_plugins_| is set to false, this instance must call
42 // SetPlugins().
43 PluginList::Singleton()->PrepareForPluginLoading();
45 BrowserThread::PostTask(BrowserThread::FILE,
46 FROM_HERE,
47 base::Bind(&PluginLoaderPosix::GetPluginsToLoad,
48 make_scoped_refptr(this)));
49 } else {
50 // If we are currently loading plugins, the plugin list might have been
51 // invalidated in the mean time, or might get invalidated before we finish.
52 // We'll wait until we have finished the current run, then try to get them
53 // again from the plugin list. If it has indeed been invalidated, it will
54 // restart plugin loading, otherwise it will immediately run the callback.
55 callbacks_.push_back(base::Bind(&PluginLoaderPosix::GetPluginsWrapper,
56 make_scoped_refptr(this), callback));
60 bool PluginLoaderPosix::OnMessageReceived(const IPC::Message& message) {
61 bool handled = true;
62 IPC_BEGIN_MESSAGE_MAP(PluginLoaderPosix, message)
63 IPC_MESSAGE_HANDLER(UtilityHostMsg_LoadedPlugin, OnPluginLoaded)
64 IPC_MESSAGE_HANDLER(UtilityHostMsg_LoadPluginFailed, OnPluginLoadFailed)
65 IPC_MESSAGE_UNHANDLED(handled = false)
66 IPC_END_MESSAGE_MAP()
67 return handled;
70 void PluginLoaderPosix::OnProcessCrashed(int exit_code) {
71 RecordAction(
72 base::UserMetricsAction("PluginLoaderPosix.UtilityProcessCrashed"));
74 if (next_load_index_ == canonical_list_.size()) {
75 // How this case occurs is unknown. See crbug.com/111935.
76 canonical_list_.clear();
77 } else {
78 canonical_list_.erase(canonical_list_.begin(),
79 canonical_list_.begin() + next_load_index_ + 1);
82 next_load_index_ = 0;
84 LoadPluginsInternal();
87 void PluginLoaderPosix::OnProcessLaunchFailed() {
88 FinishedLoadingPlugins();
91 bool PluginLoaderPosix::Send(IPC::Message* message) {
92 if (process_host_.get())
93 return process_host_->Send(message);
94 return false;
97 PluginLoaderPosix::~PluginLoaderPosix() {
100 void PluginLoaderPosix::GetPluginsToLoad() {
101 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
103 base::TimeTicks start_time(base::TimeTicks::Now());
105 loaded_plugins_.clear();
106 next_load_index_ = 0;
108 canonical_list_.clear();
109 PluginList::Singleton()->GetPluginPathsToLoad(
110 &canonical_list_,
111 PluginService::GetInstance()->NPAPIPluginsSupported());
113 internal_plugins_.clear();
114 PluginList::Singleton()->GetInternalPlugins(&internal_plugins_);
116 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
117 base::Bind(&PluginLoaderPosix::LoadPluginsInternal,
118 make_scoped_refptr(this)));
120 LOCAL_HISTOGRAM_TIMES("PluginLoaderPosix.GetPluginList",
121 (base::TimeTicks::Now() - start_time) *
122 base::Time::kMicrosecondsPerMillisecond);
125 void PluginLoaderPosix::LoadPluginsInternal() {
126 DCHECK_CURRENTLY_ON(BrowserThread::IO);
128 // Check if the list is empty or all plugins have already been loaded before
129 // forking.
130 if (IsFinishedLoadingPlugins()) {
131 FinishedLoadingPlugins();
132 return;
135 RecordAction(
136 base::UserMetricsAction("PluginLoaderPosix.LaunchUtilityProcess"));
138 UtilityProcessHostImpl* host = new UtilityProcessHostImpl(
139 this,
140 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO).get());
141 process_host_ = host->AsWeakPtr();
142 process_host_->DisableSandbox();
143 #if defined(OS_MACOSX)
144 host->set_child_flags(ChildProcessHost::CHILD_ALLOW_HEAP_EXECUTION);
145 #endif
147 bool launched = LaunchUtilityProcess();
148 if (!launched) {
149 // The utility process either failed to start or failed to receive the IPC.
150 // This process will never receive any callbacks for OnPluginLoaded() or
151 // OnPluginLoadFailed().
152 FinishedLoadingPlugins();
156 void PluginLoaderPosix::GetPluginsWrapper(
157 const PluginService::GetPluginsCallback& callback,
158 const std::vector<WebPluginInfo>& plugins_unused) {
159 // We are being called after plugin loading has finished, but we don't know
160 // whether the plugin list has been invalidated in the mean time
161 // (and therefore |plugins| might already be stale). So we simply ignore it
162 // and call regular GetPlugins() instead.
163 GetPlugins(callback);
166 void PluginLoaderPosix::OnPluginLoaded(uint32 index,
167 const WebPluginInfo& plugin) {
168 if (index != next_load_index_) {
169 LOG(ERROR) << "Received unexpected plugin load message for "
170 << plugin.path.value() << "; index=" << index;
171 return;
174 auto it = FindInternalPlugin(plugin.path);
175 if (it != internal_plugins_.end()) {
176 loaded_plugins_.push_back(*it);
177 internal_plugins_.erase(it);
178 } else {
179 loaded_plugins_.push_back(plugin);
182 ++next_load_index_;
184 if (IsFinishedLoadingPlugins())
185 FinishedLoadingPlugins();
188 void PluginLoaderPosix::OnPluginLoadFailed(uint32 index,
189 const base::FilePath& plugin_path) {
190 if (index != next_load_index_) {
191 LOG(ERROR) << "Received unexpected plugin load failure message for "
192 << plugin_path.value() << "; index=" << index;
193 return;
196 ++next_load_index_;
198 auto it = FindInternalPlugin(plugin_path);
199 if (it != internal_plugins_.end()) {
200 loaded_plugins_.push_back(*it);
201 internal_plugins_.erase(it);
204 if (IsFinishedLoadingPlugins())
205 FinishedLoadingPlugins();
208 std::vector<WebPluginInfo>::iterator PluginLoaderPosix::FindInternalPlugin(
209 const base::FilePath& plugin_path) {
210 return std::find_if(internal_plugins_.begin(), internal_plugins_.end(),
211 [&plugin_path](const WebPluginInfo& plugin) {
212 return plugin.path == plugin_path;
216 bool PluginLoaderPosix::IsFinishedLoadingPlugins() {
217 if (canonical_list_.empty())
218 return true;
220 DCHECK(next_load_index_ <= canonical_list_.size());
221 return next_load_index_ == canonical_list_.size();
224 void PluginLoaderPosix::FinishedLoadingPlugins() {
225 loading_plugins_ = false;
226 PluginList::Singleton()->SetPlugins(loaded_plugins_);
228 for (auto& callback : callbacks_) {
229 base::MessageLoop::current()->PostTask(
230 FROM_HERE, base::Bind(callback, loaded_plugins_));
232 callbacks_.clear();
235 bool PluginLoaderPosix::LaunchUtilityProcess() {
236 return process_host_->Send(new UtilityMsg_LoadPlugins(canonical_list_));
239 } // namespace content