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 base::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
= base::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 bool PluginList::PrepareForPluginLoading() {
177 base::AutoLock
lock(lock_
);
178 if (loading_state_
== LOADING_STATE_UP_TO_DATE
)
181 loading_state_
= LOADING_STATE_REFRESHING
;
185 void PluginList::LoadPlugins(bool include_npapi
) {
186 if (!PrepareForPluginLoading())
189 std::vector
<WebPluginInfo
> new_plugins
;
190 base::Closure will_load_callback
;
192 base::AutoLock
lock(lock_
);
193 will_load_callback
= will_load_plugins_callback_
;
195 if (!will_load_callback
.is_null())
196 will_load_callback
.Run();
198 std::vector
<base::FilePath
> plugin_paths
;
199 GetPluginPathsToLoad(&plugin_paths
, include_npapi
);
201 for (std::vector
<base::FilePath
>::const_iterator it
= plugin_paths
.begin();
202 it
!= plugin_paths
.end();
204 WebPluginInfo plugin_info
;
205 LoadPluginIntoPluginList(*it
, &new_plugins
, &plugin_info
);
208 SetPlugins(new_plugins
);
211 bool PluginList::LoadPluginIntoPluginList(
212 const base::FilePath
& path
,
213 std::vector
<WebPluginInfo
>* plugins
,
214 WebPluginInfo
* plugin_info
) {
215 LOG_IF(ERROR
, PluginList::DebugPluginLoading())
216 << "Loading plugin " << path
.value();
217 if (!ReadPluginInfo(path
, plugin_info
))
220 if (!ShouldLoadPluginUsingPluginList(*plugin_info
, plugins
))
223 #if defined(OS_WIN) && !defined(NDEBUG)
224 if (path
.BaseName().value() != L
"npspy.dll") // Make an exception for NPSPY
227 for (size_t i
= 0; i
< plugin_info
->mime_types
.size(); ++i
) {
228 // TODO: don't load global handlers for now.
229 // WebKit hands to the Plugin before it tries
230 // to handle mimeTypes on its own.
231 const std::string
&mime_type
= plugin_info
->mime_types
[i
].mime_type
;
232 if (mime_type
== "*")
236 plugins
->push_back(*plugin_info
);
240 void PluginList::GetPluginPathsToLoad(std::vector
<base::FilePath
>* plugin_paths
,
241 bool include_npapi
) {
242 // Don't want to hold the lock while loading new plugins, so we don't block
243 // other methods if they're called on other threads.
244 std::vector
<base::FilePath
> extra_plugin_paths
;
245 std::vector
<base::FilePath
> extra_plugin_dirs
;
247 base::AutoLock
lock(lock_
);
248 extra_plugin_paths
= extra_plugin_paths_
;
249 extra_plugin_dirs
= extra_plugin_dirs_
;
252 for (size_t i
= 0; i
< extra_plugin_paths
.size(); ++i
) {
253 const base::FilePath
& path
= extra_plugin_paths
[i
];
254 if (std::find(plugin_paths
->begin(), plugin_paths
->end(), path
) !=
255 plugin_paths
->end()) {
258 plugin_paths
->push_back(path
);
262 // A bit confusingly, this function is used to load Pepper plugins as well.
263 // Those are all internal plugins so we have to use extra_plugin_paths.
264 for (size_t i
= 0; i
< extra_plugin_dirs
.size(); ++i
)
265 GetPluginsInDir(extra_plugin_dirs
[i
], plugin_paths
);
267 std::vector
<base::FilePath
> directories_to_scan
;
268 GetPluginDirectories(&directories_to_scan
);
269 for (size_t i
= 0; i
< directories_to_scan
.size(); ++i
)
270 GetPluginsInDir(directories_to_scan
[i
], plugin_paths
);
273 GetPluginPathsFromRegistry(plugin_paths
);
278 void PluginList::SetPlugins(const std::vector
<WebPluginInfo
>& plugins
) {
279 base::AutoLock
lock(lock_
);
281 // If we haven't been invalidated in the mean time, mark the plug-in list as
283 if (loading_state_
!= LOADING_STATE_NEEDS_REFRESH
)
284 loading_state_
= LOADING_STATE_UP_TO_DATE
;
286 plugins_list_
= plugins
;
289 void PluginList::set_will_load_plugins_callback(const base::Closure
& callback
) {
290 base::AutoLock
lock(lock_
);
291 will_load_plugins_callback_
= callback
;
294 void PluginList::GetPlugins(std::vector
<WebPluginInfo
>* plugins
,
295 bool include_npapi
) {
296 LoadPlugins(include_npapi
);
297 base::AutoLock
lock(lock_
);
298 plugins
->insert(plugins
->end(), plugins_list_
.begin(), plugins_list_
.end());
301 bool PluginList::GetPluginsNoRefresh(std::vector
<WebPluginInfo
>* plugins
) {
302 base::AutoLock
lock(lock_
);
303 plugins
->insert(plugins
->end(), plugins_list_
.begin(), plugins_list_
.end());
305 return loading_state_
== LOADING_STATE_UP_TO_DATE
;
308 void PluginList::GetPluginInfoArray(
310 const std::string
& mime_type
,
314 std::vector
<WebPluginInfo
>* info
,
315 std::vector
<std::string
>* actual_mime_types
) {
316 DCHECK(mime_type
== base::StringToLowerASCII(mime_type
));
320 LoadPlugins(include_npapi
);
321 base::AutoLock
lock(lock_
);
323 *use_stale
= (loading_state_
!= LOADING_STATE_UP_TO_DATE
);
325 if (actual_mime_types
)
326 actual_mime_types
->clear();
328 std::set
<base::FilePath
> visited_plugins
;
330 // Add in plugins by mime type.
331 for (size_t i
= 0; i
< plugins_list_
.size(); ++i
) {
332 if (SupportsType(plugins_list_
[i
], mime_type
, allow_wildcard
)) {
333 base::FilePath path
= plugins_list_
[i
].path
;
334 if (visited_plugins
.insert(path
).second
) {
335 info
->push_back(plugins_list_
[i
]);
336 if (actual_mime_types
)
337 actual_mime_types
->push_back(mime_type
);
342 // Add in plugins by url.
343 // We do not permit URL-sniff based plug-in MIME type overrides aside from
344 // the case where the "type" was initially missing.
345 // We collected stats to determine this approach isn't a major compat issue,
346 // and we defend against content confusion attacks in various cases, such
347 // as when the user doesn't have the Flash plug-in enabled.
348 std::string path
= url
.path();
349 std::string::size_type last_dot
= path
.rfind('.');
350 if (last_dot
!= std::string::npos
&& mime_type
.empty()) {
351 std::string extension
=
352 base::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