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/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"
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
));
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
43 PluginList::Singleton()->PrepareForPluginLoading();
45 BrowserThread::PostTask(BrowserThread::FILE,
47 base::Bind(&PluginLoaderPosix::GetPluginsToLoad
,
48 make_scoped_refptr(this)));
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
) {
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)
70 void PluginLoaderPosix::OnProcessCrashed(int exit_code
) {
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();
78 canonical_list_
.erase(canonical_list_
.begin(),
79 canonical_list_
.begin() + next_load_index_
+ 1);
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
);
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(
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
130 if (IsFinishedLoadingPlugins()) {
131 FinishedLoadingPlugins();
136 base::UserMetricsAction("PluginLoaderPosix.LaunchUtilityProcess"));
138 UtilityProcessHostImpl
* host
= new UtilityProcessHostImpl(
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
);
147 bool launched
= LaunchUtilityProcess();
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
;
174 auto it
= FindInternalPlugin(plugin
.path
);
175 if (it
!= internal_plugins_
.end()) {
176 loaded_plugins_
.push_back(*it
);
177 internal_plugins_
.erase(it
);
179 loaded_plugins_
.push_back(plugin
);
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
;
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())
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_
));
235 bool PluginLoaderPosix::LaunchUtilityProcess() {
236 return process_host_
->Send(new UtilityMsg_LoadPlugins(canonical_list_
));
239 } // namespace content