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 "chrome/browser/plugins/plugin_finder.h"
8 #include "base/json/json_reader.h"
9 #include "base/message_loop/message_loop.h"
10 #include "base/metrics/histogram_macros.h"
11 #include "base/prefs/pref_registry_simple.h"
12 #include "base/prefs/pref_service.h"
13 #include "base/stl_util.h"
14 #include "base/strings/sys_string_conversions.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "base/values.h"
17 #include "chrome/browser/browser_process.h"
18 #include "chrome/browser/plugins/plugin_metadata.h"
19 #include "chrome/common/pref_names.h"
20 #include "content/public/browser/browser_thread.h"
21 #include "content/public/browser/plugin_service.h"
22 #include "grit/browser_resources.h"
23 #include "ui/base/resource/resource_bundle.h"
26 #if defined(ENABLE_PLUGIN_INSTALLATION)
27 #include "chrome/browser/plugins/plugin_installer.h"
30 using base::DictionaryValue
;
31 using content::PluginService
;
35 typedef std::map
<std::string
, PluginMetadata
*> PluginMap
;
37 // Do not change these values, as they are used in UMA.
38 enum class PluginListError
{
39 // NO_ERROR is defined by Windows headers.
40 PLUGIN_LIST_NO_ERROR
= 0,
41 JSON_INVALID_ESCAPE
= 1,
42 JSON_SYNTAX_ERROR
= 2,
43 JSON_UNEXPECTED_TOKEN
= 3,
44 JSON_TRAILING_COMMA
= 4,
45 JSON_TOO_MUCH_NESTING
= 5,
46 JSON_UNEXPECTED_DATA_AFTER_ROOT
= 5,
47 JSON_UNSUPPORTED_ENCODING
= 6,
48 JSON_UNQUOTED_DICTIONARY_KEY
= 7,
53 // Gets the full path of the plugin file as the identifier.
54 std::string
GetLongIdentifier(const content::WebPluginInfo
& plugin
) {
55 return plugin
.path
.AsUTF8Unsafe();
58 // Gets the base name of the file path as the identifier.
59 std::string
GetIdentifier(const content::WebPluginInfo
& plugin
) {
60 return plugin
.path
.BaseName().AsUTF8Unsafe();
63 // Gets the plugin group name as the plugin name if it is not empty or
64 // the filename without extension if the name is empty.
65 static base::string16
GetGroupName(const content::WebPluginInfo
& plugin
) {
66 if (!plugin
.name
.empty())
69 return plugin
.path
.BaseName().RemoveExtension().AsUTF16Unsafe();
72 void LoadMimeTypes(bool matching_mime_types
,
73 const base::DictionaryValue
* plugin_dict
,
74 PluginMetadata
* plugin
) {
75 const base::ListValue
* mime_types
= NULL
;
76 std::string list_key
=
77 matching_mime_types
? "matching_mime_types" : "mime_types";
78 if (!plugin_dict
->GetList(list_key
, &mime_types
))
82 for (base::ListValue::const_iterator mime_type_it
= mime_types
->begin();
83 mime_type_it
!= mime_types
->end(); ++mime_type_it
) {
84 std::string mime_type_str
;
85 success
= (*mime_type_it
)->GetAsString(&mime_type_str
);
87 if (matching_mime_types
) {
88 plugin
->AddMatchingMimeType(mime_type_str
);
90 plugin
->AddMimeType(mime_type_str
);
95 PluginMetadata
* CreatePluginMetadata(
96 const std::string
& identifier
,
97 const base::DictionaryValue
* plugin_dict
) {
99 bool success
= plugin_dict
->GetString("url", &url
);
100 std::string help_url
;
101 plugin_dict
->GetString("help_url", &help_url
);
103 success
= plugin_dict
->GetString("name", &name
);
105 bool display_url
= false;
106 plugin_dict
->GetBoolean("displayurl", &display_url
);
107 base::string16 group_name_matcher
;
108 success
= plugin_dict
->GetString("group_name_matcher", &group_name_matcher
);
110 std::string language_str
;
111 plugin_dict
->GetString("lang", &language_str
);
113 PluginMetadata
* plugin
= new PluginMetadata(identifier
,
120 const base::ListValue
* versions
= NULL
;
121 if (plugin_dict
->GetList("versions", &versions
)) {
122 for (base::ListValue::const_iterator it
= versions
->begin();
123 it
!= versions
->end(); ++it
) {
124 base::DictionaryValue
* version_dict
= NULL
;
125 if (!(*it
)->GetAsDictionary(&version_dict
)) {
130 success
= version_dict
->GetString("version", &version
);
132 std::string status_str
;
133 success
= version_dict
->GetString("status", &status_str
);
135 PluginMetadata::SecurityStatus status
=
136 PluginMetadata::SECURITY_STATUS_UP_TO_DATE
;
137 success
= PluginMetadata::ParseSecurityStatus(status_str
, &status
);
139 plugin
->AddVersion(Version(version
), status
);
143 LoadMimeTypes(false, plugin_dict
, plugin
);
144 LoadMimeTypes(true, plugin_dict
, plugin
);
148 void RecordBuiltInPluginListError(PluginListError error_code
) {
149 UMA_HISTOGRAM_ENUMERATION("PluginFinder.BuiltInPluginList.ErrorCode",
150 static_cast<int>(error_code
),
151 static_cast<int>(PluginListError::NUM_VALUES
));
157 void PluginFinder::RegisterPrefs(PrefRegistrySimple
* registry
) {
158 registry
->RegisterBooleanPref(prefs::kDisablePluginFinder
, false);
162 PluginFinder
* PluginFinder::GetInstance() {
163 // PluginFinder::GetInstance() is the only method that's allowed to call
164 // base::Singleton<PluginFinder>::get().
165 return base::Singleton
<PluginFinder
>::get();
168 PluginFinder::PluginFinder() : version_(-1) {
169 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
172 void PluginFinder::Init() {
173 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
174 // Load the built-in plugin list first. If we have a newer version stored
175 // locally or download one, we will replace this one with it.
176 scoped_ptr
<base::DictionaryValue
> plugin_list(LoadBuiltInPluginList());
178 // Gracefully handle the case where we couldn't parse the built-in plugin list
179 // for some reason (https://crbug.com/388560). TODO(bauerb): Change back to a
180 // DCHECK once we have gathered more data about the underlying problem.
184 ReinitializePlugins(plugin_list
.get());
188 base::DictionaryValue
* PluginFinder::LoadBuiltInPluginList() {
189 base::StringPiece
json_resource(
190 ResourceBundle::GetSharedInstance().GetRawDataResource(
191 IDR_PLUGIN_DB_JSON
));
192 std::string error_str
;
193 int error_code
= base::JSONReader::JSON_NO_ERROR
;
194 scoped_ptr
<base::Value
> value
= base::JSONReader::ReadAndReturnError(
195 json_resource
, base::JSON_PARSE_RFC
, &error_code
, &error_str
);
197 DLOG(ERROR
) << error_str
;
198 switch (error_code
) {
199 case base::JSONReader::JSON_INVALID_ESCAPE
:
200 RecordBuiltInPluginListError(PluginListError::JSON_INVALID_ESCAPE
);
202 case base::JSONReader::JSON_SYNTAX_ERROR
:
203 RecordBuiltInPluginListError(PluginListError::JSON_SYNTAX_ERROR
);
205 case base::JSONReader::JSON_UNEXPECTED_TOKEN
:
206 RecordBuiltInPluginListError(PluginListError::JSON_UNEXPECTED_TOKEN
);
208 case base::JSONReader::JSON_TRAILING_COMMA
:
209 RecordBuiltInPluginListError(PluginListError::JSON_TRAILING_COMMA
);
211 case base::JSONReader::JSON_TOO_MUCH_NESTING
:
212 RecordBuiltInPluginListError(PluginListError::JSON_TOO_MUCH_NESTING
);
214 case base::JSONReader::JSON_UNEXPECTED_DATA_AFTER_ROOT
:
215 RecordBuiltInPluginListError(
216 PluginListError::JSON_UNEXPECTED_DATA_AFTER_ROOT
);
218 case base::JSONReader::JSON_UNSUPPORTED_ENCODING
:
219 RecordBuiltInPluginListError(
220 PluginListError::JSON_UNSUPPORTED_ENCODING
);
222 case base::JSONReader::JSON_UNQUOTED_DICTIONARY_KEY
:
223 RecordBuiltInPluginListError(
224 PluginListError::JSON_UNQUOTED_DICTIONARY_KEY
);
226 case base::JSONReader::JSON_NO_ERROR
:
227 case base::JSONReader::JSON_PARSE_ERROR_COUNT
:
234 if (value
->GetType() != base::Value::TYPE_DICTIONARY
) {
235 // JSONReader::JSON_PARSE_ERROR_COUNT is used for the case where the JSON
236 // value has the wrong type.
237 RecordBuiltInPluginListError(PluginListError::SCHEMA_ERROR
);
241 DCHECK_EQ(base::JSONReader::JSON_NO_ERROR
, error_code
);
242 RecordBuiltInPluginListError(PluginListError::PLUGIN_LIST_NO_ERROR
);
243 return static_cast<base::DictionaryValue
*>(value
.release());
246 PluginFinder::~PluginFinder() {
247 #if defined(ENABLE_PLUGIN_INSTALLATION)
248 STLDeleteValues(&installers_
);
250 STLDeleteValues(&identifier_plugin_
);
253 base::string16
PluginFinder::FindPluginName(const std::string
& mime_type
,
254 const std::string
& language
) {
255 base::AutoLock
lock(mutex_
);
257 for (auto plugin
: identifier_plugin_
) {
258 if (language
== plugin
.second
->language() &&
259 plugin
.second
->HasMimeType(mime_type
)) {
260 return plugin
.second
->name();
264 return base::UTF8ToUTF16(mime_type
);
267 #if defined(ENABLE_PLUGIN_INSTALLATION)
268 bool PluginFinder::FindPlugin(
269 const std::string
& mime_type
,
270 const std::string
& language
,
271 PluginInstaller
** installer
,
272 scoped_ptr
<PluginMetadata
>* plugin_metadata
) {
273 if (g_browser_process
->local_state()->GetBoolean(prefs::kDisablePluginFinder
))
276 base::AutoLock
lock(mutex_
);
277 PluginMap::const_iterator metadata_it
= identifier_plugin_
.begin();
278 for (; metadata_it
!= identifier_plugin_
.end(); ++metadata_it
) {
279 if (language
== metadata_it
->second
->language() &&
280 metadata_it
->second
->HasMimeType(mime_type
)) {
281 *plugin_metadata
= metadata_it
->second
->Clone();
283 std::map
<std::string
, PluginInstaller
*>::const_iterator installer_it
=
284 installers_
.find(metadata_it
->second
->identifier());
285 DCHECK(installer_it
!= installers_
.end());
286 *installer
= installer_it
->second
;
293 bool PluginFinder::FindPluginWithIdentifier(
294 const std::string
& identifier
,
295 PluginInstaller
** installer
,
296 scoped_ptr
<PluginMetadata
>* plugin_metadata
) {
297 base::AutoLock
lock(mutex_
);
298 PluginMap::const_iterator metadata_it
= identifier_plugin_
.find(identifier
);
299 if (metadata_it
== identifier_plugin_
.end())
301 *plugin_metadata
= metadata_it
->second
->Clone();
304 std::map
<std::string
, PluginInstaller
*>::const_iterator installer_it
=
305 installers_
.find(identifier
);
306 if (installer_it
== installers_
.end())
308 *installer
= installer_it
->second
;
314 void PluginFinder::ReinitializePlugins(
315 const base::DictionaryValue
* plugin_list
) {
316 base::AutoLock
lock(mutex_
);
317 int version
= 0; // If no version is defined, we default to 0.
318 const char kVersionKey
[] = "x-version";
319 plugin_list
->GetInteger(kVersionKey
, &version
);
320 if (version
<= version_
)
325 STLDeleteValues(&identifier_plugin_
);
327 for (base::DictionaryValue::Iterator
plugin_it(*plugin_list
);
328 !plugin_it
.IsAtEnd(); plugin_it
.Advance()) {
329 const base::DictionaryValue
* plugin
= NULL
;
330 const std::string
& identifier
= plugin_it
.key();
331 if (plugin_list
->GetDictionaryWithoutPathExpansion(identifier
, &plugin
)) {
332 DCHECK(!identifier_plugin_
[identifier
]);
333 identifier_plugin_
[identifier
] = CreatePluginMetadata(identifier
, plugin
);
335 #if defined(ENABLE_PLUGIN_INSTALLATION)
336 if (installers_
.find(identifier
) == installers_
.end())
337 installers_
[identifier
] = new PluginInstaller();
343 base::string16
PluginFinder::FindPluginNameWithIdentifier(
344 const std::string
& identifier
) {
345 base::AutoLock
lock(mutex_
);
346 PluginMap::const_iterator it
= identifier_plugin_
.find(identifier
);
348 if (it
!= identifier_plugin_
.end())
349 name
= it
->second
->name();
351 return name
.empty() ? base::UTF8ToUTF16(identifier
) : name
;
354 scoped_ptr
<PluginMetadata
> PluginFinder::GetPluginMetadata(
355 const content::WebPluginInfo
& plugin
) {
356 base::AutoLock
lock(mutex_
);
357 for (PluginMap::const_iterator it
= identifier_plugin_
.begin();
358 it
!= identifier_plugin_
.end(); ++it
) {
359 if (!it
->second
->MatchesPlugin(plugin
))
362 return it
->second
->Clone();
365 // The plugin metadata was not found, create a dummy one holding
366 // the name, identifier and group name only.
367 std::string identifier
= GetIdentifier(plugin
);
368 PluginMetadata
* metadata
= new PluginMetadata(identifier
,
369 GetGroupName(plugin
),
375 for (size_t i
= 0; i
< plugin
.mime_types
.size(); ++i
)
376 metadata
->AddMatchingMimeType(plugin
.mime_types
[i
].mime_type
);
378 DCHECK(metadata
->MatchesPlugin(plugin
));
379 if (identifier_plugin_
.find(identifier
) != identifier_plugin_
.end())
380 identifier
= GetLongIdentifier(plugin
);
382 DCHECK(identifier_plugin_
.find(identifier
) == identifier_plugin_
.end());
383 identifier_plugin_
[identifier
] = metadata
;
384 return metadata
->Clone();