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"
8 #include "base/location.h"
9 #include "base/metrics/histogram.h"
10 #include "base/single_thread_task_runner.h"
11 #include "base/thread_task_runner_handle.h"
12 #include "content/browser/utility_process_host_impl.h"
13 #include "content/common/child_process_host_impl.h"
14 #include "content/common/plugin_list.h"
15 #include "content/common/utility_messages.h"
16 #include "content/public/browser/browser_thread.h"
17 #include "content/public/browser/plugin_service.h"
18 #include "content/public/browser/user_metrics.h"
22 PluginLoaderPosix::PluginLoaderPosix()
23 : next_load_index_(0), loading_plugins_(false) {
26 void PluginLoaderPosix::GetPlugins(
27 const PluginService::GetPluginsCallback
& callback
) {
28 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
30 std::vector
<WebPluginInfo
> cached_plugins
;
31 if (PluginList::Singleton()->GetPluginsNoRefresh(&cached_plugins
)) {
32 // Can't assume the caller is reentrant.
33 base::ThreadTaskRunnerHandle::Get()->PostTask(
34 FROM_HERE
, base::Bind(callback
, cached_plugins
));
38 if (!loading_plugins_
) {
39 loading_plugins_
= true;
40 callbacks_
.push_back(callback
);
42 // When |loading_plugins_| is set to false, this instance must call
44 PluginList::Singleton()->PrepareForPluginLoading();
46 BrowserThread::PostTask(BrowserThread::FILE,
48 base::Bind(&PluginLoaderPosix::GetPluginsToLoad
,
49 make_scoped_refptr(this)));
51 // If we are currently loading plugins, the plugin list might have been
52 // invalidated in the mean time, or might get invalidated before we finish.
53 // We'll wait until we have finished the current run, then try to get them
54 // again from the plugin list. If it has indeed been invalidated, it will
55 // restart plugin loading, otherwise it will immediately run the callback.
56 callbacks_
.push_back(base::Bind(&PluginLoaderPosix::GetPluginsWrapper
,
57 make_scoped_refptr(this), callback
));
61 bool PluginLoaderPosix::OnMessageReceived(const IPC::Message
& message
) {
63 IPC_BEGIN_MESSAGE_MAP(PluginLoaderPosix
, message
)
64 IPC_MESSAGE_HANDLER(UtilityHostMsg_LoadedPlugin
, OnPluginLoaded
)
65 IPC_MESSAGE_HANDLER(UtilityHostMsg_LoadPluginFailed
, OnPluginLoadFailed
)
66 IPC_MESSAGE_UNHANDLED(handled
= false)
71 void PluginLoaderPosix::OnProcessCrashed(int exit_code
) {
73 base::UserMetricsAction("PluginLoaderPosix.UtilityProcessCrashed"));
75 if (next_load_index_
== canonical_list_
.size()) {
76 // How this case occurs is unknown. See crbug.com/111935.
77 canonical_list_
.clear();
79 canonical_list_
.erase(canonical_list_
.begin(),
80 canonical_list_
.begin() + next_load_index_
+ 1);
85 LoadPluginsInternal();
88 void PluginLoaderPosix::OnProcessLaunchFailed() {
89 FinishedLoadingPlugins();
92 bool PluginLoaderPosix::Send(IPC::Message
* message
) {
93 if (process_host_
.get())
94 return process_host_
->Send(message
);
98 PluginLoaderPosix::~PluginLoaderPosix() {
101 void PluginLoaderPosix::GetPluginsToLoad() {
102 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
104 base::TimeTicks
start_time(base::TimeTicks::Now());
106 loaded_plugins_
.clear();
107 next_load_index_
= 0;
109 canonical_list_
.clear();
110 PluginList::Singleton()->GetPluginPathsToLoad(
112 PluginService::GetInstance()->NPAPIPluginsSupported());
114 internal_plugins_
.clear();
115 PluginList::Singleton()->GetInternalPlugins(&internal_plugins_
);
117 BrowserThread::PostTask(BrowserThread::IO
, FROM_HERE
,
118 base::Bind(&PluginLoaderPosix::LoadPluginsInternal
,
119 make_scoped_refptr(this)));
121 LOCAL_HISTOGRAM_TIMES("PluginLoaderPosix.GetPluginList",
122 (base::TimeTicks::Now() - start_time
) *
123 base::Time::kMicrosecondsPerMillisecond
);
126 void PluginLoaderPosix::LoadPluginsInternal() {
127 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
129 // Check if the list is empty or all plugins have already been loaded before
131 if (IsFinishedLoadingPlugins()) {
132 FinishedLoadingPlugins();
137 base::UserMetricsAction("PluginLoaderPosix.LaunchUtilityProcess"));
139 UtilityProcessHostImpl
* host
= new UtilityProcessHostImpl(
141 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO
).get());
142 process_host_
= host
->AsWeakPtr();
143 process_host_
->DisableSandbox();
144 #if defined(OS_MACOSX)
145 host
->set_child_flags(ChildProcessHost::CHILD_ALLOW_HEAP_EXECUTION
);
148 bool launched
= LaunchUtilityProcess();
150 // The utility process either failed to start or failed to receive the IPC.
151 // This process will never receive any callbacks for OnPluginLoaded() or
152 // OnPluginLoadFailed().
153 FinishedLoadingPlugins();
157 void PluginLoaderPosix::GetPluginsWrapper(
158 const PluginService::GetPluginsCallback
& callback
,
159 const std::vector
<WebPluginInfo
>& plugins_unused
) {
160 // We are being called after plugin loading has finished, but we don't know
161 // whether the plugin list has been invalidated in the mean time
162 // (and therefore |plugins| might already be stale). So we simply ignore it
163 // and call regular GetPlugins() instead.
164 GetPlugins(callback
);
167 void PluginLoaderPosix::OnPluginLoaded(uint32 index
,
168 const WebPluginInfo
& plugin
) {
169 if (index
!= next_load_index_
) {
170 LOG(ERROR
) << "Received unexpected plugin load message for "
171 << plugin
.path
.value() << "; index=" << index
;
175 auto it
= FindInternalPlugin(plugin
.path
);
176 if (it
!= internal_plugins_
.end()) {
177 loaded_plugins_
.push_back(*it
);
178 internal_plugins_
.erase(it
);
180 loaded_plugins_
.push_back(plugin
);
185 if (IsFinishedLoadingPlugins())
186 FinishedLoadingPlugins();
189 void PluginLoaderPosix::OnPluginLoadFailed(uint32 index
,
190 const base::FilePath
& plugin_path
) {
191 if (index
!= next_load_index_
) {
192 LOG(ERROR
) << "Received unexpected plugin load failure message for "
193 << plugin_path
.value() << "; index=" << index
;
199 auto it
= FindInternalPlugin(plugin_path
);
200 if (it
!= internal_plugins_
.end()) {
201 loaded_plugins_
.push_back(*it
);
202 internal_plugins_
.erase(it
);
205 if (IsFinishedLoadingPlugins())
206 FinishedLoadingPlugins();
209 std::vector
<WebPluginInfo
>::iterator
PluginLoaderPosix::FindInternalPlugin(
210 const base::FilePath
& plugin_path
) {
211 return std::find_if(internal_plugins_
.begin(), internal_plugins_
.end(),
212 [&plugin_path
](const WebPluginInfo
& plugin
) {
213 return plugin
.path
== plugin_path
;
217 bool PluginLoaderPosix::IsFinishedLoadingPlugins() {
218 if (canonical_list_
.empty())
221 DCHECK(next_load_index_
<= canonical_list_
.size());
222 return next_load_index_
== canonical_list_
.size();
225 void PluginLoaderPosix::FinishedLoadingPlugins() {
226 loading_plugins_
= false;
227 PluginList::Singleton()->SetPlugins(loaded_plugins_
);
229 for (auto& callback
: callbacks_
) {
230 base::ThreadTaskRunnerHandle::Get()->PostTask(
231 FROM_HERE
, base::Bind(callback
, loaded_plugins_
));
236 bool PluginLoaderPosix::LaunchUtilityProcess() {
237 return process_host_
->Send(new UtilityMsg_LoadPlugins(canonical_list_
));
240 } // namespace content