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
= base::SplitString(
134 mime_types_str
, "|", base::TRIM_WHITESPACE
, base::SPLIT_WANT_ALL
);
135 std::vector
<std::string
> file_extensions
= base::SplitString(
136 file_extensions_str
, "|", base::TRIM_WHITESPACE
, base::SPLIT_WANT_ALL
);
137 std::vector
<base::string16
> descriptions
= base::SplitString(
138 mime_type_descriptions_str
, base::string16(1, '|'), base::TRIM_WHITESPACE
,
139 base::SPLIT_WANT_ALL
);
141 parsed_mime_types
->clear();
143 if (mime_types
.empty())
146 for (size_t i
= 0; i
< mime_types
.size(); ++i
) {
147 WebPluginMimeType mime_type
;
148 mime_type
.mime_type
= base::ToLowerASCII(mime_types
[i
]);
149 if (file_extensions
.size() > i
) {
150 mime_type
.file_extensions
= base::SplitString(
151 file_extensions
[i
], ",", base::TRIM_WHITESPACE
, base::SPLIT_WANT_ALL
);
154 if (descriptions
.size() > i
) {
155 mime_type
.description
= descriptions
[i
];
157 // On Windows, the description likely has a list of file extensions
158 // embedded in it (e.g. "SurfWriter file (*.swr)"). Remove an extension
159 // list from the description if it is present.
160 size_t ext
= mime_type
.description
.find(base::ASCIIToUTF16("(*"));
161 if (ext
!= base::string16::npos
) {
162 if (ext
> 1 && mime_type
.description
[ext
- 1] == ' ')
165 mime_type
.description
.erase(ext
);
169 parsed_mime_types
->push_back(mime_type
);
175 PluginList::PluginList()
176 : loading_state_(LOADING_STATE_NEEDS_REFRESH
),
177 plugins_discovery_disabled_(false) {
180 bool PluginList::PrepareForPluginLoading() {
181 base::AutoLock
lock(lock_
);
182 if (loading_state_
== LOADING_STATE_UP_TO_DATE
)
185 loading_state_
= LOADING_STATE_REFRESHING
;
189 void PluginList::LoadPlugins(bool include_npapi
) {
190 if (!PrepareForPluginLoading())
193 std::vector
<WebPluginInfo
> new_plugins
;
194 base::Closure will_load_callback
;
196 base::AutoLock
lock(lock_
);
197 will_load_callback
= will_load_plugins_callback_
;
199 if (!will_load_callback
.is_null())
200 will_load_callback
.Run();
202 std::vector
<base::FilePath
> plugin_paths
;
203 GetPluginPathsToLoad(&plugin_paths
, include_npapi
);
205 for (std::vector
<base::FilePath
>::const_iterator it
= plugin_paths
.begin();
206 it
!= plugin_paths
.end();
208 WebPluginInfo plugin_info
;
209 LoadPluginIntoPluginList(*it
, &new_plugins
, &plugin_info
);
212 SetPlugins(new_plugins
);
215 bool PluginList::LoadPluginIntoPluginList(
216 const base::FilePath
& path
,
217 std::vector
<WebPluginInfo
>* plugins
,
218 WebPluginInfo
* plugin_info
) {
219 LOG_IF(ERROR
, PluginList::DebugPluginLoading())
220 << "Loading plugin " << path
.value();
221 if (!ReadPluginInfo(path
, plugin_info
))
224 if (!ShouldLoadPluginUsingPluginList(*plugin_info
, plugins
))
227 #if defined(OS_WIN) && !defined(NDEBUG)
228 if (path
.BaseName().value() != L
"npspy.dll") // Make an exception for NPSPY
231 for (size_t i
= 0; i
< plugin_info
->mime_types
.size(); ++i
) {
232 // TODO: don't load global handlers for now.
233 // WebKit hands to the Plugin before it tries
234 // to handle mimeTypes on its own.
235 const std::string
&mime_type
= plugin_info
->mime_types
[i
].mime_type
;
236 if (mime_type
== "*")
240 plugins
->push_back(*plugin_info
);
244 void PluginList::GetPluginPathsToLoad(std::vector
<base::FilePath
>* plugin_paths
,
245 bool include_npapi
) {
246 // Don't want to hold the lock while loading new plugins, so we don't block
247 // other methods if they're called on other threads.
248 std::vector
<base::FilePath
> extra_plugin_paths
;
249 std::vector
<base::FilePath
> extra_plugin_dirs
;
251 base::AutoLock
lock(lock_
);
252 extra_plugin_paths
= extra_plugin_paths_
;
253 extra_plugin_dirs
= extra_plugin_dirs_
;
256 for (size_t i
= 0; i
< extra_plugin_paths
.size(); ++i
) {
257 const base::FilePath
& path
= extra_plugin_paths
[i
];
258 if (std::find(plugin_paths
->begin(), plugin_paths
->end(), path
) !=
259 plugin_paths
->end()) {
262 plugin_paths
->push_back(path
);
266 // A bit confusingly, this function is used to load Pepper plugins as well.
267 // Those are all internal plugins so we have to use extra_plugin_paths.
268 for (size_t i
= 0; i
< extra_plugin_dirs
.size(); ++i
)
269 GetPluginsInDir(extra_plugin_dirs
[i
], plugin_paths
);
271 std::vector
<base::FilePath
> directories_to_scan
;
272 GetPluginDirectories(&directories_to_scan
);
273 for (size_t i
= 0; i
< directories_to_scan
.size(); ++i
)
274 GetPluginsInDir(directories_to_scan
[i
], plugin_paths
);
277 GetPluginPathsFromRegistry(plugin_paths
);
282 void PluginList::SetPlugins(const std::vector
<WebPluginInfo
>& plugins
) {
283 base::AutoLock
lock(lock_
);
285 // If we haven't been invalidated in the mean time, mark the plugin list as
287 if (loading_state_
!= LOADING_STATE_NEEDS_REFRESH
)
288 loading_state_
= LOADING_STATE_UP_TO_DATE
;
290 plugins_list_
= plugins
;
293 void PluginList::set_will_load_plugins_callback(const base::Closure
& callback
) {
294 base::AutoLock
lock(lock_
);
295 will_load_plugins_callback_
= callback
;
298 void PluginList::GetPlugins(std::vector
<WebPluginInfo
>* plugins
,
299 bool include_npapi
) {
300 LoadPlugins(include_npapi
);
301 base::AutoLock
lock(lock_
);
302 plugins
->insert(plugins
->end(), plugins_list_
.begin(), plugins_list_
.end());
305 bool PluginList::GetPluginsNoRefresh(std::vector
<WebPluginInfo
>* plugins
) {
306 base::AutoLock
lock(lock_
);
307 plugins
->insert(plugins
->end(), plugins_list_
.begin(), plugins_list_
.end());
309 return loading_state_
== LOADING_STATE_UP_TO_DATE
;
312 void PluginList::GetPluginInfoArray(
314 const std::string
& mime_type
,
318 std::vector
<WebPluginInfo
>* info
,
319 std::vector
<std::string
>* actual_mime_types
) {
320 DCHECK(mime_type
== base::ToLowerASCII(mime_type
));
324 LoadPlugins(include_npapi
);
325 base::AutoLock
lock(lock_
);
327 *use_stale
= (loading_state_
!= LOADING_STATE_UP_TO_DATE
);
329 if (actual_mime_types
)
330 actual_mime_types
->clear();
332 std::set
<base::FilePath
> visited_plugins
;
334 // Add in plugins by mime type.
335 for (size_t i
= 0; i
< plugins_list_
.size(); ++i
) {
336 if (SupportsType(plugins_list_
[i
], mime_type
, allow_wildcard
)) {
337 base::FilePath path
= plugins_list_
[i
].path
;
338 if (visited_plugins
.insert(path
).second
) {
339 info
->push_back(plugins_list_
[i
]);
340 if (actual_mime_types
)
341 actual_mime_types
->push_back(mime_type
);
346 // Add in plugins by url.
347 // We do not permit URL-sniff based plugin MIME type overrides aside from
348 // the case where the "type" was initially missing.
349 // We collected stats to determine this approach isn't a major compat issue,
350 // and we defend against content confusion attacks in various cases, such
351 // as when the user doesn't have the Flash plugin enabled.
352 std::string path
= url
.path();
353 std::string::size_type last_dot
= path
.rfind('.');
354 if (last_dot
!= std::string::npos
&& mime_type
.empty()) {
355 std::string extension
=
356 base::ToLowerASCII(base::StringPiece(path
).substr(last_dot
+ 1));
357 std::string actual_mime_type
;
358 for (size_t i
= 0; i
< plugins_list_
.size(); ++i
) {
359 if (SupportsExtension(plugins_list_
[i
], extension
, &actual_mime_type
)) {
360 base::FilePath path
= plugins_list_
[i
].path
;
361 if (visited_plugins
.insert(path
).second
) {
362 info
->push_back(plugins_list_
[i
]);
363 if (actual_mime_types
)
364 actual_mime_types
->push_back(actual_mime_type
);
371 bool PluginList::SupportsType(const WebPluginInfo
& plugin
,
372 const std::string
& mime_type
,
373 bool allow_wildcard
) {
374 // Webkit will ask for a plugin to handle empty mime types.
375 if (mime_type
.empty())
378 for (size_t i
= 0; i
< plugin
.mime_types
.size(); ++i
) {
379 const WebPluginMimeType
& mime_info
= plugin
.mime_types
[i
];
380 if (net::MatchesMimeType(mime_info
.mime_type
, mime_type
)) {
381 if (!allow_wildcard
&& mime_info
.mime_type
== "*")
389 bool PluginList::SupportsExtension(const WebPluginInfo
& plugin
,
390 const std::string
& extension
,
391 std::string
* actual_mime_type
) {
392 for (size_t i
= 0; i
< plugin
.mime_types
.size(); ++i
) {
393 const WebPluginMimeType
& mime_type
= plugin
.mime_types
[i
];
394 for (size_t j
= 0; j
< mime_type
.file_extensions
.size(); ++j
) {
395 if (mime_type
.file_extensions
[j
] == extension
) {
396 if (actual_mime_type
)
397 *actual_mime_type
= mime_type
.mime_type
;
405 void PluginList::RemoveExtraPluginPathLocked(
406 const base::FilePath
& plugin_path
) {
407 lock_
.AssertAcquired();
408 std::vector
<base::FilePath
>::iterator it
=
409 std::find(extra_plugin_paths_
.begin(), extra_plugin_paths_
.end(),
411 if (it
!= extra_plugin_paths_
.end())
412 extra_plugin_paths_
.erase(it
);
415 PluginList::~PluginList() {
419 } // namespace content