Add ICU message format support
[chromium-blink-merge.git] / chrome / browser / plugins / plugin_finder.cc
blobdc6ec0598259b408a911a85fdc29a49422f1780c
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"
7 #include "base/bind.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"
24 #include "url/gurl.h"
26 #if defined(ENABLE_PLUGIN_INSTALLATION)
27 #include "chrome/browser/plugins/plugin_installer.h"
28 #endif
30 using base::DictionaryValue;
31 using content::PluginService;
33 namespace {
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,
49 SCHEMA_ERROR = 8,
50 NUM_VALUES
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())
67 return plugin.name;
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))
79 return;
81 bool success = false;
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);
86 DCHECK(success);
87 if (matching_mime_types) {
88 plugin->AddMatchingMimeType(mime_type_str);
89 } else {
90 plugin->AddMimeType(mime_type_str);
95 PluginMetadata* CreatePluginMetadata(
96 const std::string& identifier,
97 const base::DictionaryValue* plugin_dict) {
98 std::string url;
99 bool success = plugin_dict->GetString("url", &url);
100 std::string help_url;
101 plugin_dict->GetString("help_url", &help_url);
102 base::string16 name;
103 success = plugin_dict->GetString("name", &name);
104 DCHECK(success);
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);
109 DCHECK(success);
110 std::string language_str;
111 plugin_dict->GetString("lang", &language_str);
113 PluginMetadata* plugin = new PluginMetadata(identifier,
114 name,
115 display_url,
116 GURL(url),
117 GURL(help_url),
118 group_name_matcher,
119 language_str);
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)) {
126 NOTREACHED();
127 continue;
129 std::string version;
130 success = version_dict->GetString("version", &version);
131 DCHECK(success);
132 std::string status_str;
133 success = version_dict->GetString("status", &status_str);
134 DCHECK(success);
135 PluginMetadata::SecurityStatus status =
136 PluginMetadata::SECURITY_STATUS_UP_TO_DATE;
137 success = PluginMetadata::ParseSecurityStatus(status_str, &status);
138 DCHECK(success);
139 plugin->AddVersion(Version(version), status);
143 LoadMimeTypes(false, plugin_dict, plugin);
144 LoadMimeTypes(true, plugin_dict, plugin);
145 return 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));
154 } // namespace
156 // static
157 void PluginFinder::RegisterPrefs(PrefRegistrySimple* registry) {
158 registry->RegisterBooleanPref(prefs::kDisablePluginFinder, false);
161 // static
162 PluginFinder* PluginFinder::GetInstance() {
163 // PluginFinder::GetInstance() is the only method that's allowed to call
164 // Singleton<PluginFinder>::get().
165 return 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.
181 if (!plugin_list)
182 return;
184 ReinitializePlugins(plugin_list.get());
187 // static
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);
196 if (!value) {
197 DLOG(ERROR) << error_str;
198 switch (error_code) {
199 case base::JSONReader::JSON_INVALID_ESCAPE:
200 RecordBuiltInPluginListError(PluginListError::JSON_INVALID_ESCAPE);
201 break;
202 case base::JSONReader::JSON_SYNTAX_ERROR:
203 RecordBuiltInPluginListError(PluginListError::JSON_SYNTAX_ERROR);
204 break;
205 case base::JSONReader::JSON_UNEXPECTED_TOKEN:
206 RecordBuiltInPluginListError(PluginListError::JSON_UNEXPECTED_TOKEN);
207 break;
208 case base::JSONReader::JSON_TRAILING_COMMA:
209 RecordBuiltInPluginListError(PluginListError::JSON_TRAILING_COMMA);
210 break;
211 case base::JSONReader::JSON_TOO_MUCH_NESTING:
212 RecordBuiltInPluginListError(PluginListError::JSON_TOO_MUCH_NESTING);
213 break;
214 case base::JSONReader::JSON_UNEXPECTED_DATA_AFTER_ROOT:
215 RecordBuiltInPluginListError(
216 PluginListError::JSON_UNEXPECTED_DATA_AFTER_ROOT);
217 break;
218 case base::JSONReader::JSON_UNSUPPORTED_ENCODING:
219 RecordBuiltInPluginListError(
220 PluginListError::JSON_UNSUPPORTED_ENCODING);
221 break;
222 case base::JSONReader::JSON_UNQUOTED_DICTIONARY_KEY:
223 RecordBuiltInPluginListError(
224 PluginListError::JSON_UNQUOTED_DICTIONARY_KEY);
225 break;
226 case base::JSONReader::JSON_NO_ERROR:
227 case base::JSONReader::JSON_PARSE_ERROR_COUNT:
228 NOTREACHED();
229 break;
231 return nullptr;
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);
238 return nullptr;
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_);
249 #endif
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))
274 return false;
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;
287 return true;
290 return false;
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())
300 return false;
301 *plugin_metadata = metadata_it->second->Clone();
303 if (installer) {
304 std::map<std::string, PluginInstaller*>::const_iterator installer_it =
305 installers_.find(identifier);
306 if (installer_it == installers_.end())
307 return false;
308 *installer = installer_it->second;
310 return true;
312 #endif
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_)
321 return;
323 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();
338 #endif
343 base::string16 PluginFinder::FindPluginNameWithIdentifier(
344 const std::string& identifier) {
345 base::AutoLock lock(mutex_);
346 PluginMap::const_iterator it = identifier_plugin_.find(identifier);
347 base::string16 name;
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))
360 continue;
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),
370 false,
371 GURL(),
372 GURL(),
373 plugin.name,
374 std::string());
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();