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/common/plugin_list.h"
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 "content/public/common/content_switches.h"
17 #include "net/base/mime_util.h"
21 #include "content/common/plugin_constants_win.h"
28 base::LazyInstance
<PluginList
> g_singleton
= LAZY_INSTANCE_INITIALIZER
;
33 PluginList
* PluginList::Singleton() {
34 return g_singleton
.Pointer();
38 bool PluginList::DebugPluginLoading() {
39 return CommandLine::ForCurrentProcess()->HasSwitch(
40 switches::kDebugPluginLoading
);
43 void PluginList::DisablePluginsDiscovery() {
44 plugins_discovery_disabled_
= true;
47 void PluginList::RefreshPlugins() {
48 base::AutoLock
lock(lock_
);
49 loading_state_
= LOADING_STATE_NEEDS_REFRESH
;
52 void PluginList::AddExtraPluginPath(const base::FilePath
& plugin_path
) {
53 // Chrome OS only loads plugins from /opt/google/chrome/plugins.
54 #if !defined(OS_CHROMEOS)
55 base::AutoLock
lock(lock_
);
56 extra_plugin_paths_
.push_back(plugin_path
);
60 void PluginList::RemoveExtraPluginPath(const base::FilePath
& plugin_path
) {
61 base::AutoLock
lock(lock_
);
62 RemoveExtraPluginPathLocked(plugin_path
);
65 void PluginList::AddExtraPluginDir(const base::FilePath
& plugin_dir
) {
66 // Chrome OS only loads plugins from /opt/google/chrome/plugins.
67 #if !defined(OS_CHROMEOS)
68 base::AutoLock
lock(lock_
);
69 extra_plugin_dirs_
.push_back(plugin_dir
);
73 void PluginList::RegisterInternalPlugin(const WebPluginInfo
& info
,
74 bool add_at_beginning
) {
75 base::AutoLock
lock(lock_
);
77 internal_plugins_
.push_back(info
);
78 if (add_at_beginning
) {
79 // Newer registrations go earlier in the list so they can override the MIME
80 // types of older registrations.
81 extra_plugin_paths_
.insert(extra_plugin_paths_
.begin(), info
.path
);
83 extra_plugin_paths_
.push_back(info
.path
);
87 void PluginList::UnregisterInternalPlugin(const base::FilePath
& path
) {
88 base::AutoLock
lock(lock_
);
90 for (size_t i
= 0; i
< internal_plugins_
.size(); i
++) {
91 if (internal_plugins_
[i
].path
== path
) {
92 internal_plugins_
.erase(internal_plugins_
.begin() + i
);
98 RemoveExtraPluginPathLocked(path
);
101 void PluginList::GetInternalPlugins(
102 std::vector
<WebPluginInfo
>* internal_plugins
) {
103 base::AutoLock
lock(lock_
);
105 for (std::vector
<WebPluginInfo
>::iterator it
= internal_plugins_
.begin();
106 it
!= internal_plugins_
.end();
108 internal_plugins
->push_back(*it
);
112 bool PluginList::ReadPluginInfo(const base::FilePath
& filename
,
113 WebPluginInfo
* info
) {
115 base::AutoLock
lock(lock_
);
116 for (size_t i
= 0; i
< internal_plugins_
.size(); ++i
) {
117 if (filename
== internal_plugins_
[i
].path
) {
118 *info
= internal_plugins_
[i
];
124 return PluginList::ReadWebPluginInfo(filename
, info
);
128 bool PluginList::ParseMimeTypes(
129 const std::string
& mime_types_str
,
130 const std::string
& file_extensions_str
,
131 const base::string16
& mime_type_descriptions_str
,
132 std::vector
<WebPluginMimeType
>* parsed_mime_types
) {
133 std::vector
<std::string
> mime_types
, file_extensions
;
134 std::vector
<base::string16
> descriptions
;
135 base::SplitString(mime_types_str
, '|', &mime_types
);
136 base::SplitString(file_extensions_str
, '|', &file_extensions
);
137 base::SplitString(mime_type_descriptions_str
, '|', &descriptions
);
139 parsed_mime_types
->clear();
141 if (mime_types
.empty())
144 for (size_t i
= 0; i
< mime_types
.size(); ++i
) {
145 WebPluginMimeType mime_type
;
146 mime_type
.mime_type
= StringToLowerASCII(mime_types
[i
]);
147 if (file_extensions
.size() > i
)
148 base::SplitString(file_extensions
[i
], ',', &mime_type
.file_extensions
);
150 if (descriptions
.size() > i
) {
151 mime_type
.description
= descriptions
[i
];
153 // On Windows, the description likely has a list of file extensions
154 // embedded in it (e.g. "SurfWriter file (*.swr)"). Remove an extension
155 // list from the description if it is present.
156 size_t ext
= mime_type
.description
.find(base::ASCIIToUTF16("(*"));
157 if (ext
!= base::string16::npos
) {
158 if (ext
> 1 && mime_type
.description
[ext
- 1] == ' ')
161 mime_type
.description
.erase(ext
);
165 parsed_mime_types
->push_back(mime_type
);
171 PluginList::PluginList()
172 : loading_state_(LOADING_STATE_NEEDS_REFRESH
),
173 plugins_discovery_disabled_(false) {
176 void PluginList::LoadPlugins(bool include_npapi
) {
178 base::AutoLock
lock(lock_
);
179 if (loading_state_
== LOADING_STATE_UP_TO_DATE
)
182 loading_state_
= LOADING_STATE_REFRESHING
;
185 std::vector
<WebPluginInfo
> new_plugins
;
186 base::Closure will_load_callback
;
188 base::AutoLock
lock(lock_
);
189 will_load_callback
= will_load_plugins_callback_
;
191 if (!will_load_callback
.is_null())
192 will_load_callback
.Run();
194 std::vector
<base::FilePath
> plugin_paths
;
195 GetPluginPathsToLoad(&plugin_paths
, include_npapi
);
197 for (std::vector
<base::FilePath
>::const_iterator it
= plugin_paths
.begin();
198 it
!= plugin_paths
.end();
200 WebPluginInfo plugin_info
;
201 LoadPluginIntoPluginList(*it
, &new_plugins
, &plugin_info
);
204 base::AutoLock
lock(lock_
);
205 plugins_list_
.swap(new_plugins
);
207 // If we haven't been invalidated in the mean time, mark the plug-in list as
209 if (loading_state_
!= LOADING_STATE_NEEDS_REFRESH
)
210 loading_state_
= LOADING_STATE_UP_TO_DATE
;
213 bool PluginList::LoadPluginIntoPluginList(
214 const base::FilePath
& path
,
215 std::vector
<WebPluginInfo
>* plugins
,
216 WebPluginInfo
* plugin_info
) {
217 LOG_IF(ERROR
, PluginList::DebugPluginLoading())
218 << "Loading plugin " << path
.value();
219 if (!ReadPluginInfo(path
, plugin_info
))
222 if (!ShouldLoadPluginUsingPluginList(*plugin_info
, plugins
))
225 #if defined(OS_WIN) && !defined(NDEBUG)
226 if (path
.BaseName().value() != L
"npspy.dll") // Make an exception for NPSPY
229 for (size_t i
= 0; i
< plugin_info
->mime_types
.size(); ++i
) {
230 // TODO: don't load global handlers for now.
231 // WebKit hands to the Plugin before it tries
232 // to handle mimeTypes on its own.
233 const std::string
&mime_type
= plugin_info
->mime_types
[i
].mime_type
;
234 if (mime_type
== "*")
238 plugins
->push_back(*plugin_info
);
242 void PluginList::GetPluginPathsToLoad(std::vector
<base::FilePath
>* plugin_paths
,
243 bool include_npapi
) {
244 // Don't want to hold the lock while loading new plugins, so we don't block
245 // other methods if they're called on other threads.
246 std::vector
<base::FilePath
> extra_plugin_paths
;
247 std::vector
<base::FilePath
> extra_plugin_dirs
;
249 base::AutoLock
lock(lock_
);
250 extra_plugin_paths
= extra_plugin_paths_
;
251 extra_plugin_dirs
= extra_plugin_dirs_
;
254 for (size_t i
= 0; i
< extra_plugin_paths
.size(); ++i
) {
255 const base::FilePath
& path
= extra_plugin_paths
[i
];
256 if (std::find(plugin_paths
->begin(), plugin_paths
->end(), path
) !=
257 plugin_paths
->end()) {
260 plugin_paths
->push_back(path
);
264 // A bit confusingly, this function is used to load Pepper plugins as well.
265 // Those are all internal plugins so we have to use extra_plugin_paths.
266 for (size_t i
= 0; i
< extra_plugin_dirs
.size(); ++i
)
267 GetPluginsInDir(extra_plugin_dirs
[i
], plugin_paths
);
269 std::vector
<base::FilePath
> directories_to_scan
;
270 GetPluginDirectories(&directories_to_scan
);
271 for (size_t i
= 0; i
< directories_to_scan
.size(); ++i
)
272 GetPluginsInDir(directories_to_scan
[i
], plugin_paths
);
275 GetPluginPathsFromRegistry(plugin_paths
);
280 void PluginList::SetPlugins(const std::vector
<WebPluginInfo
>& plugins
) {
281 base::AutoLock
lock(lock_
);
283 DCHECK_NE(LOADING_STATE_REFRESHING
, loading_state_
);
284 loading_state_
= LOADING_STATE_UP_TO_DATE
;
286 plugins_list_
.clear();
287 plugins_list_
.insert(plugins_list_
.end(), plugins
.begin(), plugins
.end());
290 void PluginList::set_will_load_plugins_callback(const base::Closure
& callback
) {
291 base::AutoLock
lock(lock_
);
292 will_load_plugins_callback_
= callback
;
295 void PluginList::GetPlugins(std::vector
<WebPluginInfo
>* plugins
,
296 bool include_npapi
) {
297 LoadPlugins(include_npapi
);
298 base::AutoLock
lock(lock_
);
299 plugins
->insert(plugins
->end(), plugins_list_
.begin(), plugins_list_
.end());
302 bool PluginList::GetPluginsNoRefresh(std::vector
<WebPluginInfo
>* plugins
) {
303 base::AutoLock
lock(lock_
);
304 plugins
->insert(plugins
->end(), plugins_list_
.begin(), plugins_list_
.end());
306 return loading_state_
== LOADING_STATE_UP_TO_DATE
;
309 void PluginList::GetPluginInfoArray(
311 const std::string
& mime_type
,
315 std::vector
<WebPluginInfo
>* info
,
316 std::vector
<std::string
>* actual_mime_types
) {
317 DCHECK(mime_type
== StringToLowerASCII(mime_type
));
321 LoadPlugins(include_npapi
);
322 base::AutoLock
lock(lock_
);
324 *use_stale
= (loading_state_
!= LOADING_STATE_UP_TO_DATE
);
326 if (actual_mime_types
)
327 actual_mime_types
->clear();
329 std::set
<base::FilePath
> visited_plugins
;
331 // Add in plugins by mime type.
332 for (size_t i
= 0; i
< plugins_list_
.size(); ++i
) {
333 if (SupportsType(plugins_list_
[i
], mime_type
, allow_wildcard
)) {
334 base::FilePath path
= plugins_list_
[i
].path
;
335 if (visited_plugins
.insert(path
).second
) {
336 info
->push_back(plugins_list_
[i
]);
337 if (actual_mime_types
)
338 actual_mime_types
->push_back(mime_type
);
343 // Add in plugins by url.
344 // We do not permit URL-sniff based plug-in MIME type overrides aside from
345 // the case where the "type" was initially missing.
346 // We collected stats to determine this approach isn't a major compat issue,
347 // and we defend against content confusion attacks in various cases, such
348 // as when the user doesn't have the Flash plug-in enabled.
349 std::string path
= url
.path();
350 std::string::size_type last_dot
= path
.rfind('.');
351 if (last_dot
!= std::string::npos
&& mime_type
.empty()) {
352 std::string extension
= StringToLowerASCII(std::string(path
, last_dot
+1));
353 std::string actual_mime_type
;
354 for (size_t i
= 0; i
< plugins_list_
.size(); ++i
) {
355 if (SupportsExtension(plugins_list_
[i
], extension
, &actual_mime_type
)) {
356 base::FilePath path
= plugins_list_
[i
].path
;
357 if (visited_plugins
.insert(path
).second
) {
358 info
->push_back(plugins_list_
[i
]);
359 if (actual_mime_types
)
360 actual_mime_types
->push_back(actual_mime_type
);
367 bool PluginList::SupportsType(const WebPluginInfo
& plugin
,
368 const std::string
& mime_type
,
369 bool allow_wildcard
) {
370 // Webkit will ask for a plugin to handle empty mime types.
371 if (mime_type
.empty())
374 for (size_t i
= 0; i
< plugin
.mime_types
.size(); ++i
) {
375 const WebPluginMimeType
& mime_info
= plugin
.mime_types
[i
];
376 if (net::MatchesMimeType(mime_info
.mime_type
, mime_type
)) {
377 if (!allow_wildcard
&& mime_info
.mime_type
== "*")
385 bool PluginList::SupportsExtension(const WebPluginInfo
& plugin
,
386 const std::string
& extension
,
387 std::string
* actual_mime_type
) {
388 for (size_t i
= 0; i
< plugin
.mime_types
.size(); ++i
) {
389 const WebPluginMimeType
& mime_type
= plugin
.mime_types
[i
];
390 for (size_t j
= 0; j
< mime_type
.file_extensions
.size(); ++j
) {
391 if (mime_type
.file_extensions
[j
] == extension
) {
392 if (actual_mime_type
)
393 *actual_mime_type
= mime_type
.mime_type
;
401 void PluginList::RemoveExtraPluginPathLocked(
402 const base::FilePath
& plugin_path
) {
403 lock_
.AssertAcquired();
404 std::vector
<base::FilePath
>::iterator it
=
405 std::find(extra_plugin_paths_
.begin(), extra_plugin_paths_
.end(),
407 if (it
!= extra_plugin_paths_
.end())
408 extra_plugin_paths_
.erase(it
);
411 PluginList::~PluginList() {
415 } // namespace content