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 "webkit/plugins/npapi/plugin_list.h" | |
6 | |
7 #include <algorithm> | |
8 | |
9 #include "base/command_line.h" | |
10 #include "base/lazy_instance.h" | |
11 #include "base/logging.h" | |
12 #include "base/strings/string_split.h" | |
13 #include "base/strings/string_util.h" | |
14 #include "base/strings/sys_string_conversions.h" | |
15 #include "base/strings/utf_string_conversions.h" | |
16 #include "net/base/mime_util.h" | |
17 #include "url/gurl.h" | |
18 #include "webkit/plugins/npapi/plugin_utils.h" | |
19 #include "webkit/plugins/plugin_switches.h" | |
20 | |
21 #if defined(OS_WIN) | |
22 #include "webkit/plugins/npapi/plugin_constants_win.h" | |
23 #endif | |
24 | |
25 namespace { | |
26 | |
27 using webkit::npapi::PluginList; | |
28 | |
29 const char kApplicationOctetStream[] = "application/octet-stream"; | |
30 | |
31 base::LazyInstance<PluginList> g_singleton = LAZY_INSTANCE_INITIALIZER; | |
32 | |
33 bool AllowMimeTypeMismatch(const std::string& orig_mime_type, | |
34 const std::string& actual_mime_type) { | |
35 if (orig_mime_type == actual_mime_type) { | |
36 NOTREACHED(); | |
37 return true; | |
38 } | |
39 | |
40 // We do not permit URL-sniff based plug-in MIME type overrides aside from | |
41 // the case where the "type" was initially missing or generic | |
42 // (application/octet-stream). | |
43 // We collected stats to determine this approach isn't a major compat issue, | |
44 // and we defend against content confusion attacks in various cases, such | |
45 // as when the user doesn't have the Flash plug-in enabled. | |
46 bool allow = orig_mime_type.empty() || | |
47 orig_mime_type == kApplicationOctetStream; | |
48 LOG_IF(INFO, !allow) << "Ignoring plugin with unexpected MIME type " | |
49 << actual_mime_type << " (expected " << orig_mime_type | |
50 << ")"; | |
51 return allow; | |
52 } | |
53 | |
54 } // namespace | |
55 | |
56 namespace webkit { | |
57 namespace npapi { | |
58 | |
59 // static | |
60 PluginList* PluginList::Singleton() { | |
61 return g_singleton.Pointer(); | |
62 } | |
63 | |
64 // static | |
65 bool PluginList::DebugPluginLoading() { | |
66 return CommandLine::ForCurrentProcess()->HasSwitch( | |
67 switches::kDebugPluginLoading); | |
68 } | |
69 | |
70 void PluginList::DisablePluginsDiscovery() { | |
71 plugins_discovery_disabled_ = true; | |
72 } | |
73 | |
74 void PluginList::RefreshPlugins() { | |
75 base::AutoLock lock(lock_); | |
76 loading_state_ = LOADING_STATE_NEEDS_REFRESH; | |
77 } | |
78 | |
79 void PluginList::AddExtraPluginPath(const base::FilePath& plugin_path) { | |
80 if (!NPAPIPluginsSupported()) { | |
81 // TODO(jam): remove and just have CHECK once we're sure this doesn't get | |
82 // triggered. | |
83 DLOG(INFO) << "NPAPI plugins not supported"; | |
84 return; | |
85 } | |
86 | |
87 // Chrome OS only loads plugins from /opt/google/chrome/plugins. | |
88 #if !defined(OS_CHROMEOS) | |
89 base::AutoLock lock(lock_); | |
90 extra_plugin_paths_.push_back(plugin_path); | |
91 #endif | |
92 } | |
93 | |
94 void PluginList::RemoveExtraPluginPath(const base::FilePath& plugin_path) { | |
95 base::AutoLock lock(lock_); | |
96 std::vector<base::FilePath>::iterator it = | |
97 std::find(extra_plugin_paths_.begin(), extra_plugin_paths_.end(), | |
98 plugin_path); | |
99 if (it != extra_plugin_paths_.end()) | |
100 extra_plugin_paths_.erase(it); | |
101 } | |
102 | |
103 void PluginList::AddExtraPluginDir(const base::FilePath& plugin_dir) { | |
104 // Chrome OS only loads plugins from /opt/google/chrome/plugins. | |
105 #if !defined(OS_CHROMEOS) | |
106 base::AutoLock lock(lock_); | |
107 extra_plugin_dirs_.push_back(plugin_dir); | |
108 #endif | |
109 } | |
110 | |
111 void PluginList::RegisterInternalPlugin(const webkit::WebPluginInfo& info, | |
112 bool add_at_beginning) { | |
113 if (!NPAPIPluginsSupported() && | |
114 info.type == WebPluginInfo::PLUGIN_TYPE_NPAPI) { | |
115 DLOG(INFO) << "Don't register NPAPI plugins when they're not supported"; | |
116 return; | |
117 } | |
118 | |
119 base::AutoLock lock(lock_); | |
120 | |
121 internal_plugins_.push_back(info); | |
122 if (add_at_beginning) { | |
123 // Newer registrations go earlier in the list so they can override the MIME | |
124 // types of older registrations. | |
125 extra_plugin_paths_.insert(extra_plugin_paths_.begin(), info.path); | |
126 } else { | |
127 extra_plugin_paths_.push_back(info.path); | |
128 } | |
129 } | |
130 | |
131 void PluginList::UnregisterInternalPlugin(const base::FilePath& path) { | |
132 base::AutoLock lock(lock_); | |
133 for (size_t i = 0; i < internal_plugins_.size(); i++) { | |
134 if (internal_plugins_[i].path == path) { | |
135 internal_plugins_.erase(internal_plugins_.begin() + i); | |
136 return; | |
137 } | |
138 } | |
139 NOTREACHED(); | |
140 } | |
141 | |
142 void PluginList::GetInternalPlugins( | |
143 std::vector<webkit::WebPluginInfo>* internal_plugins) { | |
144 base::AutoLock lock(lock_); | |
145 | |
146 for (std::vector<webkit::WebPluginInfo>::iterator it = | |
147 internal_plugins_.begin(); | |
148 it != internal_plugins_.end(); | |
149 ++it) { | |
150 internal_plugins->push_back(*it); | |
151 } | |
152 } | |
153 | |
154 bool PluginList::ReadPluginInfo(const base::FilePath& filename, | |
155 webkit::WebPluginInfo* info) { | |
156 { | |
157 base::AutoLock lock(lock_); | |
158 for (size_t i = 0; i < internal_plugins_.size(); ++i) { | |
159 if (filename == internal_plugins_[i].path) { | |
160 *info = internal_plugins_[i]; | |
161 return true; | |
162 } | |
163 } | |
164 } | |
165 | |
166 return PluginList::ReadWebPluginInfo(filename, info); | |
167 } | |
168 | |
169 // static | |
170 bool PluginList::ParseMimeTypes( | |
171 const std::string& mime_types_str, | |
172 const std::string& file_extensions_str, | |
173 const base::string16& mime_type_descriptions_str, | |
174 std::vector<webkit::WebPluginMimeType>* parsed_mime_types) { | |
175 std::vector<std::string> mime_types, file_extensions; | |
176 std::vector<base::string16> descriptions; | |
177 base::SplitString(mime_types_str, '|', &mime_types); | |
178 base::SplitString(file_extensions_str, '|', &file_extensions); | |
179 base::SplitString(mime_type_descriptions_str, '|', &descriptions); | |
180 | |
181 parsed_mime_types->clear(); | |
182 | |
183 if (mime_types.empty()) | |
184 return false; | |
185 | |
186 for (size_t i = 0; i < mime_types.size(); ++i) { | |
187 WebPluginMimeType mime_type; | |
188 mime_type.mime_type = StringToLowerASCII(mime_types[i]); | |
189 if (file_extensions.size() > i) | |
190 base::SplitString(file_extensions[i], ',', &mime_type.file_extensions); | |
191 | |
192 if (descriptions.size() > i) { | |
193 mime_type.description = descriptions[i]; | |
194 | |
195 // On Windows, the description likely has a list of file extensions | |
196 // embedded in it (e.g. "SurfWriter file (*.swr)"). Remove an extension | |
197 // list from the description if it is present. | |
198 size_t ext = mime_type.description.find(ASCIIToUTF16("(*")); | |
199 if (ext != base::string16::npos) { | |
200 if (ext > 1 && mime_type.description[ext - 1] == ' ') | |
201 ext--; | |
202 | |
203 mime_type.description.erase(ext); | |
204 } | |
205 } | |
206 | |
207 parsed_mime_types->push_back(mime_type); | |
208 } | |
209 | |
210 return true; | |
211 } | |
212 | |
213 PluginList::PluginList() | |
214 : loading_state_(LOADING_STATE_NEEDS_REFRESH), | |
215 plugins_discovery_disabled_(false) { | |
216 } | |
217 | |
218 void PluginList::LoadPluginsIntoPluginListInternal( | |
219 std::vector<webkit::WebPluginInfo>* plugins) { | |
220 base::Closure will_load_callback; | |
221 { | |
222 base::AutoLock lock(lock_); | |
223 will_load_callback = will_load_plugins_callback_; | |
224 } | |
225 if (!will_load_callback.is_null()) | |
226 will_load_callback.Run(); | |
227 | |
228 std::vector<base::FilePath> plugin_paths; | |
229 GetPluginPathsToLoad(&plugin_paths); | |
230 | |
231 for (std::vector<base::FilePath>::const_iterator it = plugin_paths.begin(); | |
232 it != plugin_paths.end(); | |
233 ++it) { | |
234 WebPluginInfo plugin_info; | |
235 LoadPluginIntoPluginList(*it, plugins, &plugin_info); | |
236 } | |
237 } | |
238 | |
239 void PluginList::LoadPlugins() { | |
240 { | |
241 base::AutoLock lock(lock_); | |
242 if (loading_state_ == LOADING_STATE_UP_TO_DATE) | |
243 return; | |
244 | |
245 loading_state_ = LOADING_STATE_REFRESHING; | |
246 } | |
247 | |
248 std::vector<webkit::WebPluginInfo> new_plugins; | |
249 // Do the actual loading of the plugins. | |
250 LoadPluginsIntoPluginListInternal(&new_plugins); | |
251 | |
252 base::AutoLock lock(lock_); | |
253 plugins_list_.swap(new_plugins); | |
254 | |
255 // If we haven't been invalidated in the mean time, mark the plug-in list as | |
256 // up-to-date. | |
257 if (loading_state_ != LOADING_STATE_NEEDS_REFRESH) | |
258 loading_state_ = LOADING_STATE_UP_TO_DATE; | |
259 } | |
260 | |
261 bool PluginList::LoadPluginIntoPluginList( | |
262 const base::FilePath& path, | |
263 std::vector<webkit::WebPluginInfo>* plugins, | |
264 WebPluginInfo* plugin_info) { | |
265 LOG_IF(ERROR, PluginList::DebugPluginLoading()) | |
266 << "Loading plugin " << path.value(); | |
267 if (!ReadPluginInfo(path, plugin_info)) | |
268 return false; | |
269 | |
270 if (!ShouldLoadPluginUsingPluginList(*plugin_info, plugins)) | |
271 return false; | |
272 | |
273 #if defined(OS_WIN) && !defined(NDEBUG) | |
274 if (path.BaseName().value() != L"npspy.dll") // Make an exception for NPSPY | |
275 #endif | |
276 { | |
277 for (size_t i = 0; i < plugin_info->mime_types.size(); ++i) { | |
278 // TODO: don't load global handlers for now. | |
279 // WebKit hands to the Plugin before it tries | |
280 // to handle mimeTypes on its own. | |
281 const std::string &mime_type = plugin_info->mime_types[i].mime_type; | |
282 if (mime_type == "*") | |
283 return false; | |
284 } | |
285 } | |
286 plugins->push_back(*plugin_info); | |
287 return true; | |
288 } | |
289 | |
290 void PluginList::GetPluginPathsToLoad(std::vector<base::FilePath>* plugin_paths)
{ | |
291 // Don't want to hold the lock while loading new plugins, so we don't block | |
292 // other methods if they're called on other threads. | |
293 std::vector<base::FilePath> extra_plugin_paths; | |
294 std::vector<base::FilePath> extra_plugin_dirs; | |
295 { | |
296 base::AutoLock lock(lock_); | |
297 extra_plugin_paths = extra_plugin_paths_; | |
298 extra_plugin_dirs = extra_plugin_dirs_; | |
299 } | |
300 | |
301 for (size_t i = 0; i < extra_plugin_paths.size(); ++i) { | |
302 const base::FilePath& path = extra_plugin_paths[i]; | |
303 if (std::find(plugin_paths->begin(), plugin_paths->end(), path) != | |
304 plugin_paths->end()) { | |
305 continue; | |
306 } | |
307 plugin_paths->push_back(path); | |
308 } | |
309 | |
310 if (NPAPIPluginsSupported()) { | |
311 // A bit confusingly, this function is used to load Pepper plugins as well. | |
312 // Those are all internal plugins so we have to use extra_plugin_paths. | |
313 for (size_t i = 0; i < extra_plugin_dirs.size(); ++i) | |
314 GetPluginsInDir(extra_plugin_dirs[i], plugin_paths); | |
315 | |
316 std::vector<base::FilePath> directories_to_scan; | |
317 GetPluginDirectories(&directories_to_scan); | |
318 for (size_t i = 0; i < directories_to_scan.size(); ++i) | |
319 GetPluginsInDir(directories_to_scan[i], plugin_paths); | |
320 | |
321 #if defined(OS_WIN) | |
322 GetPluginPathsFromRegistry(plugin_paths); | |
323 #endif | |
324 } | |
325 } | |
326 | |
327 void PluginList::SetPlugins(const std::vector<webkit::WebPluginInfo>& plugins) { | |
328 base::AutoLock lock(lock_); | |
329 | |
330 DCHECK_NE(LOADING_STATE_REFRESHING, loading_state_); | |
331 loading_state_ = LOADING_STATE_UP_TO_DATE; | |
332 | |
333 plugins_list_.clear(); | |
334 plugins_list_.insert(plugins_list_.end(), plugins.begin(), plugins.end()); | |
335 } | |
336 | |
337 void PluginList::set_will_load_plugins_callback(const base::Closure& callback) { | |
338 base::AutoLock lock(lock_); | |
339 will_load_plugins_callback_ = callback; | |
340 } | |
341 | |
342 void PluginList::GetPlugins(std::vector<WebPluginInfo>* plugins) { | |
343 LoadPlugins(); | |
344 base::AutoLock lock(lock_); | |
345 plugins->insert(plugins->end(), plugins_list_.begin(), plugins_list_.end()); | |
346 } | |
347 | |
348 bool PluginList::GetPluginsNoRefresh( | |
349 std::vector<webkit::WebPluginInfo>* plugins) { | |
350 base::AutoLock lock(lock_); | |
351 plugins->insert(plugins->end(), plugins_list_.begin(), plugins_list_.end()); | |
352 | |
353 return loading_state_ == LOADING_STATE_UP_TO_DATE; | |
354 } | |
355 | |
356 void PluginList::GetPluginInfoArray( | |
357 const GURL& url, | |
358 const std::string& mime_type, | |
359 bool allow_wildcard, | |
360 bool* use_stale, | |
361 std::vector<webkit::WebPluginInfo>* info, | |
362 std::vector<std::string>* actual_mime_types) { | |
363 DCHECK(mime_type == StringToLowerASCII(mime_type)); | |
364 DCHECK(info); | |
365 | |
366 if (!use_stale) | |
367 LoadPlugins(); | |
368 base::AutoLock lock(lock_); | |
369 if (use_stale) | |
370 *use_stale = (loading_state_ != LOADING_STATE_UP_TO_DATE); | |
371 info->clear(); | |
372 if (actual_mime_types) | |
373 actual_mime_types->clear(); | |
374 | |
375 std::set<base::FilePath> visited_plugins; | |
376 | |
377 // Add in plugins by mime type. | |
378 for (size_t i = 0; i < plugins_list_.size(); ++i) { | |
379 if (SupportsType(plugins_list_[i], mime_type, allow_wildcard)) { | |
380 base::FilePath path = plugins_list_[i].path; | |
381 if (visited_plugins.insert(path).second) { | |
382 info->push_back(plugins_list_[i]); | |
383 if (actual_mime_types) | |
384 actual_mime_types->push_back(mime_type); | |
385 } | |
386 } | |
387 } | |
388 | |
389 // Add in plugins by url. | |
390 std::string path = url.path(); | |
391 std::string::size_type last_dot = path.rfind('.'); | |
392 if (last_dot != std::string::npos) { | |
393 std::string extension = StringToLowerASCII(std::string(path, last_dot+1)); | |
394 std::string actual_mime_type; | |
395 for (size_t i = 0; i < plugins_list_.size(); ++i) { | |
396 if (SupportsExtension(plugins_list_[i], extension, &actual_mime_type)) { | |
397 base::FilePath path = plugins_list_[i].path; | |
398 if (visited_plugins.insert(path).second && | |
399 AllowMimeTypeMismatch(mime_type, actual_mime_type)) { | |
400 info->push_back(plugins_list_[i]); | |
401 if (actual_mime_types) | |
402 actual_mime_types->push_back(actual_mime_type); | |
403 } | |
404 } | |
405 } | |
406 } | |
407 } | |
408 | |
409 bool PluginList::SupportsType(const webkit::WebPluginInfo& plugin, | |
410 const std::string& mime_type, | |
411 bool allow_wildcard) { | |
412 // Webkit will ask for a plugin to handle empty mime types. | |
413 if (mime_type.empty()) | |
414 return false; | |
415 | |
416 for (size_t i = 0; i < plugin.mime_types.size(); ++i) { | |
417 const webkit::WebPluginMimeType& mime_info = plugin.mime_types[i]; | |
418 if (net::MatchesMimeType(mime_info.mime_type, mime_type)) { | |
419 if (!allow_wildcard && mime_info.mime_type == "*") | |
420 continue; | |
421 return true; | |
422 } | |
423 } | |
424 return false; | |
425 } | |
426 | |
427 bool PluginList::SupportsExtension(const webkit::WebPluginInfo& plugin, | |
428 const std::string& extension, | |
429 std::string* actual_mime_type) { | |
430 for (size_t i = 0; i < plugin.mime_types.size(); ++i) { | |
431 const webkit::WebPluginMimeType& mime_type = plugin.mime_types[i]; | |
432 for (size_t j = 0; j < mime_type.file_extensions.size(); ++j) { | |
433 if (mime_type.file_extensions[j] == extension) { | |
434 if (actual_mime_type) | |
435 *actual_mime_type = mime_type.mime_type; | |
436 return true; | |
437 } | |
438 } | |
439 } | |
440 return false; | |
441 } | |
442 | |
443 PluginList::~PluginList() { | |
444 } | |
445 | |
446 | |
447 } // namespace npapi | |
448 } // namespace webkit | |
OLD | NEW |