OLD | NEW |
| (Empty) |
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. | |
4 | |
5 #include "content/browser/plugin_loader_posix.h" | |
6 | |
7 #include "base/bind.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" | |
19 | |
20 namespace content { | |
21 | |
22 PluginLoaderPosix::PluginLoaderPosix() | |
23 : next_load_index_(0), loading_plugins_(false) { | |
24 } | |
25 | |
26 void PluginLoaderPosix::GetPlugins( | |
27 const PluginService::GetPluginsCallback& callback) { | |
28 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
29 | |
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)); | |
35 return; | |
36 } | |
37 | |
38 if (!loading_plugins_) { | |
39 loading_plugins_ = true; | |
40 callbacks_.push_back(callback); | |
41 | |
42 // When |loading_plugins_| is set to false, this instance must call | |
43 // SetPlugins(). | |
44 PluginList::Singleton()->PrepareForPluginLoading(); | |
45 | |
46 BrowserThread::PostTask(BrowserThread::FILE, | |
47 FROM_HERE, | |
48 base::Bind(&PluginLoaderPosix::GetPluginsToLoad, | |
49 make_scoped_refptr(this))); | |
50 } else { | |
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)); | |
58 } | |
59 } | |
60 | |
61 bool PluginLoaderPosix::OnMessageReceived(const IPC::Message& message) { | |
62 bool handled = true; | |
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) | |
67 IPC_END_MESSAGE_MAP() | |
68 return handled; | |
69 } | |
70 | |
71 void PluginLoaderPosix::OnProcessCrashed(int exit_code) { | |
72 RecordAction( | |
73 base::UserMetricsAction("PluginLoaderPosix.UtilityProcessCrashed")); | |
74 | |
75 if (next_load_index_ == canonical_list_.size()) { | |
76 // How this case occurs is unknown. See crbug.com/111935. | |
77 canonical_list_.clear(); | |
78 } else { | |
79 canonical_list_.erase(canonical_list_.begin(), | |
80 canonical_list_.begin() + next_load_index_ + 1); | |
81 } | |
82 | |
83 next_load_index_ = 0; | |
84 | |
85 LoadPluginsInternal(); | |
86 } | |
87 | |
88 void PluginLoaderPosix::OnProcessLaunchFailed() { | |
89 FinishedLoadingPlugins(); | |
90 } | |
91 | |
92 bool PluginLoaderPosix::Send(IPC::Message* message) { | |
93 if (process_host_.get()) | |
94 return process_host_->Send(message); | |
95 return false; | |
96 } | |
97 | |
98 PluginLoaderPosix::~PluginLoaderPosix() { | |
99 } | |
100 | |
101 void PluginLoaderPosix::GetPluginsToLoad() { | |
102 DCHECK_CURRENTLY_ON(BrowserThread::FILE); | |
103 | |
104 base::TimeTicks start_time(base::TimeTicks::Now()); | |
105 | |
106 loaded_plugins_.clear(); | |
107 next_load_index_ = 0; | |
108 | |
109 canonical_list_.clear(); | |
110 PluginList::Singleton()->GetPluginPathsToLoad( | |
111 &canonical_list_, | |
112 PluginService::GetInstance()->NPAPIPluginsSupported()); | |
113 | |
114 internal_plugins_.clear(); | |
115 PluginList::Singleton()->GetInternalPlugins(&internal_plugins_); | |
116 | |
117 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, | |
118 base::Bind(&PluginLoaderPosix::LoadPluginsInternal, | |
119 make_scoped_refptr(this))); | |
120 | |
121 LOCAL_HISTOGRAM_TIMES("PluginLoaderPosix.GetPluginList", | |
122 (base::TimeTicks::Now() - start_time) * | |
123 base::Time::kMicrosecondsPerMillisecond); | |
124 } | |
125 | |
126 void PluginLoaderPosix::LoadPluginsInternal() { | |
127 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
128 | |
129 // Check if the list is empty or all plugins have already been loaded before | |
130 // forking. | |
131 if (IsFinishedLoadingPlugins()) { | |
132 FinishedLoadingPlugins(); | |
133 return; | |
134 } | |
135 | |
136 RecordAction( | |
137 base::UserMetricsAction("PluginLoaderPosix.LaunchUtilityProcess")); | |
138 | |
139 UtilityProcessHostImpl* host = new UtilityProcessHostImpl( | |
140 this, | |
141 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO).get()); | |
142 process_host_ = host->AsWeakPtr(); | |
143 process_host_->DisableSandbox(); | |
144 | |
145 bool launched = LaunchUtilityProcess(); | |
146 if (!launched) { | |
147 // The utility process either failed to start or failed to receive the IPC. | |
148 // This process will never receive any callbacks for OnPluginLoaded() or | |
149 // OnPluginLoadFailed(). | |
150 FinishedLoadingPlugins(); | |
151 } | |
152 } | |
153 | |
154 void PluginLoaderPosix::GetPluginsWrapper( | |
155 const PluginService::GetPluginsCallback& callback, | |
156 const std::vector<WebPluginInfo>& plugins_unused) { | |
157 // We are being called after plugin loading has finished, but we don't know | |
158 // whether the plugin list has been invalidated in the mean time | |
159 // (and therefore |plugins| might already be stale). So we simply ignore it | |
160 // and call regular GetPlugins() instead. | |
161 GetPlugins(callback); | |
162 } | |
163 | |
164 void PluginLoaderPosix::OnPluginLoaded(uint32_t index, | |
165 const WebPluginInfo& plugin) { | |
166 if (index != next_load_index_) { | |
167 LOG(ERROR) << "Received unexpected plugin load message for " | |
168 << plugin.path.value() << "; index=" << index; | |
169 return; | |
170 } | |
171 | |
172 auto it = FindInternalPlugin(plugin.path); | |
173 if (it != internal_plugins_.end()) { | |
174 loaded_plugins_.push_back(*it); | |
175 internal_plugins_.erase(it); | |
176 } else { | |
177 loaded_plugins_.push_back(plugin); | |
178 } | |
179 | |
180 ++next_load_index_; | |
181 | |
182 if (IsFinishedLoadingPlugins()) | |
183 FinishedLoadingPlugins(); | |
184 } | |
185 | |
186 void PluginLoaderPosix::OnPluginLoadFailed(uint32_t index, | |
187 const base::FilePath& plugin_path) { | |
188 if (index != next_load_index_) { | |
189 LOG(ERROR) << "Received unexpected plugin load failure message for " | |
190 << plugin_path.value() << "; index=" << index; | |
191 return; | |
192 } | |
193 | |
194 ++next_load_index_; | |
195 | |
196 auto it = FindInternalPlugin(plugin_path); | |
197 if (it != internal_plugins_.end()) { | |
198 loaded_plugins_.push_back(*it); | |
199 internal_plugins_.erase(it); | |
200 } | |
201 | |
202 if (IsFinishedLoadingPlugins()) | |
203 FinishedLoadingPlugins(); | |
204 } | |
205 | |
206 std::vector<WebPluginInfo>::iterator PluginLoaderPosix::FindInternalPlugin( | |
207 const base::FilePath& plugin_path) { | |
208 return std::find_if(internal_plugins_.begin(), internal_plugins_.end(), | |
209 [&plugin_path](const WebPluginInfo& plugin) { | |
210 return plugin.path == plugin_path; | |
211 }); | |
212 } | |
213 | |
214 bool PluginLoaderPosix::IsFinishedLoadingPlugins() { | |
215 if (canonical_list_.empty()) | |
216 return true; | |
217 | |
218 DCHECK(next_load_index_ <= canonical_list_.size()); | |
219 return next_load_index_ == canonical_list_.size(); | |
220 } | |
221 | |
222 void PluginLoaderPosix::FinishedLoadingPlugins() { | |
223 loading_plugins_ = false; | |
224 PluginList::Singleton()->SetPlugins(loaded_plugins_); | |
225 | |
226 for (auto& callback : callbacks_) { | |
227 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
228 FROM_HERE, base::Bind(callback, loaded_plugins_)); | |
229 } | |
230 callbacks_.clear(); | |
231 } | |
232 | |
233 bool PluginLoaderPosix::LaunchUtilityProcess() { | |
234 return process_host_->Send(new UtilityMsg_LoadPlugins(canonical_list_)); | |
235 } | |
236 | |
237 } // namespace content | |
OLD | NEW |