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/ui/webui/plugins_ui.h"
11 #include "base/bind.h"
12 #include "base/bind_helpers.h"
13 #include "base/memory/ref_counted_memory.h"
14 #include "base/memory/scoped_ptr.h"
15 #include "base/memory/singleton.h"
16 #include "base/memory/weak_ptr.h"
17 #include "base/message_loop/message_loop.h"
18 #include "base/path_service.h"
19 #include "base/prefs/pref_member.h"
20 #include "base/prefs/pref_service.h"
21 #include "base/prefs/scoped_user_pref_update.h"
22 #include "base/strings/utf_string_conversions.h"
23 #include "base/values.h"
24 #include "chrome/browser/chrome_notification_types.h"
25 #include "chrome/browser/content_settings/host_content_settings_map.h"
26 #include "chrome/browser/plugins/plugin_finder.h"
27 #include "chrome/browser/plugins/plugin_metadata.h"
28 #include "chrome/browser/plugins/plugin_prefs.h"
29 #include "chrome/browser/profiles/profile.h"
30 #include "chrome/browser/ui/browser.h"
31 #include "chrome/browser/ui/browser_window.h"
32 #include "chrome/common/chrome_content_client.h"
33 #include "chrome/common/chrome_paths.h"
34 #include "chrome/common/pref_names.h"
35 #include "chrome/common/url_constants.h"
36 #include "components/user_prefs/pref_registry_syncable.h"
37 #include "content/public/browser/notification_source.h"
38 #include "content/public/browser/plugin_service.h"
39 #include "content/public/browser/web_contents.h"
40 #include "content/public/browser/web_ui.h"
41 #include "content/public/browser/web_ui_data_source.h"
42 #include "content/public/browser/web_ui_message_handler.h"
43 #include "grit/browser_resources.h"
44 #include "grit/generated_resources.h"
45 #include "grit/theme_resources.h"
46 #include "ui/base/l10n/l10n_util.h"
47 #include "ui/base/resource/resource_bundle.h"
49 #if defined(OS_CHROMEOS)
50 #include "chrome/browser/ui/webui/chromeos/ui_account_tweaks.h"
53 using content::PluginService;
54 using content::WebContents;
55 using content::WebPluginInfo;
56 using content::WebUIMessageHandler;
60 // Callback function to process result of EnablePlugin method.
61 void AssertPluginEnabled(bool did_enable) {
65 content::WebUIDataSource* CreatePluginsUIHTMLSource() {
66 content::WebUIDataSource* source =
67 content::WebUIDataSource::Create(chrome::kChromeUIPluginsHost);
68 source->SetUseJsonJSFormatV2();
70 source->AddLocalizedString("pluginsTitle", IDS_PLUGINS_TITLE);
71 source->AddLocalizedString("pluginsDetailsModeLink",
72 IDS_PLUGINS_DETAILS_MODE_LINK);
73 source->AddLocalizedString("pluginsNoneInstalled",
74 IDS_PLUGINS_NONE_INSTALLED);
75 source->AddLocalizedString("pluginDisabled", IDS_PLUGINS_DISABLED_PLUGIN);
76 source->AddLocalizedString("pluginDisabledByPolicy",
77 IDS_PLUGINS_DISABLED_BY_POLICY_PLUGIN);
78 source->AddLocalizedString("pluginEnabledByPolicy",
79 IDS_PLUGINS_ENABLED_BY_POLICY_PLUGIN);
80 source->AddLocalizedString("pluginGroupManagedByPolicy",
81 IDS_PLUGINS_GROUP_MANAGED_BY_POLICY);
82 source->AddLocalizedString("pluginDownload", IDS_PLUGINS_DOWNLOAD);
83 source->AddLocalizedString("pluginName", IDS_PLUGINS_NAME);
84 source->AddLocalizedString("pluginVersion", IDS_PLUGINS_VERSION);
85 source->AddLocalizedString("pluginDescription", IDS_PLUGINS_DESCRIPTION);
86 source->AddLocalizedString("pluginPath", IDS_PLUGINS_PATH);
87 source->AddLocalizedString("pluginType", IDS_PLUGINS_TYPE);
88 source->AddLocalizedString("pluginMimeTypes", IDS_PLUGINS_MIME_TYPES);
89 source->AddLocalizedString("pluginMimeTypesMimeType",
90 IDS_PLUGINS_MIME_TYPES_MIME_TYPE);
91 source->AddLocalizedString("pluginMimeTypesDescription",
92 IDS_PLUGINS_MIME_TYPES_DESCRIPTION);
93 source->AddLocalizedString("pluginMimeTypesFileExtensions",
94 IDS_PLUGINS_MIME_TYPES_FILE_EXTENSIONS);
95 source->AddLocalizedString("disable", IDS_PLUGINS_DISABLE);
96 source->AddLocalizedString("enable", IDS_PLUGINS_ENABLE);
97 source->AddLocalizedString("alwaysAllowed", IDS_PLUGINS_ALWAYS_ALLOWED);
98 source->AddLocalizedString("noPlugins", IDS_PLUGINS_NO_PLUGINS);
100 source->SetJsonPath("strings.js");
101 source->AddResourcePath("plugins.js", IDR_PLUGINS_JS);
102 source->SetDefaultResource(IDR_PLUGINS_HTML);
103 #if defined(OS_CHROMEOS)
104 chromeos::AddAccountUITweaksLocalizedValues(source);
109 string16 PluginTypeToString(int type) {
110 // The type is stored as an |int|, but doing the switch on the right
111 // enumeration type gives us better build-time error checking (if someone adds
113 switch (static_cast<WebPluginInfo::PluginType>(type)) {
114 case WebPluginInfo::PLUGIN_TYPE_NPAPI:
115 return l10n_util::GetStringUTF16(IDS_PLUGINS_NPAPI);
116 case WebPluginInfo::PLUGIN_TYPE_PEPPER_IN_PROCESS:
117 return l10n_util::GetStringUTF16(IDS_PLUGINS_PPAPI_IN_PROCESS);
118 case WebPluginInfo::PLUGIN_TYPE_PEPPER_OUT_OF_PROCESS:
119 return l10n_util::GetStringUTF16(IDS_PLUGINS_PPAPI_OUT_OF_PROCESS);
120 case WebPluginInfo::PLUGIN_TYPE_PEPPER_UNSANDBOXED:
121 return l10n_util::GetStringUTF16(IDS_PLUGINS_PPAPI_UNSANDBOXED);
127 ////////////////////////////////////////////////////////////////////////////////
131 ////////////////////////////////////////////////////////////////////////////////
133 // The handler for Javascript messages for the chrome://plugins/ page.
134 // TODO(viettrungluu): Make plugin list updates notify, and then observe
135 // changes; maybe replumb plugin list through plugin service?
136 // <http://crbug.com/39101>
137 class PluginsDOMHandler : public WebUIMessageHandler,
138 public content::NotificationObserver {
140 explicit PluginsDOMHandler();
141 virtual ~PluginsDOMHandler() {}
143 // WebUIMessageHandler implementation.
144 virtual void RegisterMessages() OVERRIDE;
146 // Callback for the "requestPluginsData" message.
147 void HandleRequestPluginsData(const ListValue* args);
149 // Callback for the "enablePlugin" message.
150 void HandleEnablePluginMessage(const ListValue* args);
152 // Callback for the "saveShowDetailsToPrefs" message.
153 void HandleSaveShowDetailsToPrefs(const ListValue* args);
155 // Calback for the "getShowDetails" message.
156 void HandleGetShowDetails(const ListValue* args);
158 // Callback for the "setPluginAlwaysAllowed" message.
159 void HandleSetPluginAlwaysAllowed(const ListValue* args);
161 // content::NotificationObserver method overrides
162 virtual void Observe(int type,
163 const content::NotificationSource& source,
164 const content::NotificationDetails& details) OVERRIDE;
169 // Called on the UI thread when the plugin information is ready.
170 void PluginsLoaded(const std::vector<WebPluginInfo>& plugins);
172 content::NotificationRegistrar registrar_;
174 // Holds grouped plug-ins. The key is the group identifier and
175 // the value is the list of plug-ins belonging to the group.
176 typedef base::hash_map<std::string, std::vector<const WebPluginInfo*> >
179 // This pref guards the value whether about:plugins is in the details mode or
181 BooleanPrefMember show_details_;
183 base::WeakPtrFactory<PluginsDOMHandler> weak_ptr_factory_;
185 DISALLOW_COPY_AND_ASSIGN(PluginsDOMHandler);
188 PluginsDOMHandler::PluginsDOMHandler()
189 : weak_ptr_factory_(this) {
192 void PluginsDOMHandler::RegisterMessages() {
193 Profile* profile = Profile::FromWebUI(web_ui());
195 PrefService* prefs = profile->GetPrefs();
196 show_details_.Init(prefs::kPluginsShowDetails, prefs);
199 chrome::NOTIFICATION_PLUGIN_ENABLE_STATUS_CHANGED,
200 content::Source<Profile>(profile));
202 web_ui()->RegisterMessageCallback("requestPluginsData",
203 base::Bind(&PluginsDOMHandler::HandleRequestPluginsData,
204 base::Unretained(this)));
205 web_ui()->RegisterMessageCallback("enablePlugin",
206 base::Bind(&PluginsDOMHandler::HandleEnablePluginMessage,
207 base::Unretained(this)));
208 web_ui()->RegisterMessageCallback("setPluginAlwaysAllowed",
209 base::Bind(&PluginsDOMHandler::HandleSetPluginAlwaysAllowed,
210 base::Unretained(this)));
211 web_ui()->RegisterMessageCallback("saveShowDetailsToPrefs",
212 base::Bind(&PluginsDOMHandler::HandleSaveShowDetailsToPrefs,
213 base::Unretained(this)));
214 web_ui()->RegisterMessageCallback("getShowDetails",
215 base::Bind(&PluginsDOMHandler::HandleGetShowDetails,
216 base::Unretained(this)));
219 void PluginsDOMHandler::HandleRequestPluginsData(const ListValue* args) {
223 void PluginsDOMHandler::HandleEnablePluginMessage(const ListValue* args) {
224 Profile* profile = Profile::FromWebUI(web_ui());
226 // Be robust in accepting badness since plug-ins display HTML (hence
228 if (args->GetSize() != 3) {
233 std::string enable_str;
234 std::string is_group_str;
235 if (!args->GetString(1, &enable_str) || !args->GetString(2, &is_group_str)) {
239 bool enable = enable_str == "true";
241 PluginPrefs* plugin_prefs = PluginPrefs::GetForProfile(profile).get();
242 if (is_group_str == "true") {
244 if (!args->GetString(0, &group_name)) {
249 plugin_prefs->EnablePluginGroup(enable, group_name);
251 // See http://crbug.com/50105 for background.
252 string16 adobereader = ASCIIToUTF16(
253 PluginMetadata::kAdobeReaderGroupName);
254 string16 internalpdf =
255 ASCIIToUTF16(chrome::ChromeContentClient::kPDFPluginName);
256 if (group_name == adobereader)
257 plugin_prefs->EnablePluginGroup(false, internalpdf);
258 else if (group_name == internalpdf)
259 plugin_prefs->EnablePluginGroup(false, adobereader);
262 base::FilePath::StringType file_path;
263 if (!args->GetString(0, &file_path)) {
268 plugin_prefs->EnablePlugin(enable, base::FilePath(file_path),
269 base::Bind(&AssertPluginEnabled));
273 void PluginsDOMHandler::HandleSaveShowDetailsToPrefs(const ListValue* args) {
274 std::string details_mode;
275 if (!args->GetString(0, &details_mode)) {
279 show_details_.SetValue(details_mode == "true");
282 void PluginsDOMHandler::HandleGetShowDetails(const ListValue* args) {
283 base::FundamentalValue show_details(show_details_.GetValue());
284 web_ui()->CallJavascriptFunction("loadShowDetailsFromPrefs", show_details);
287 void PluginsDOMHandler::HandleSetPluginAlwaysAllowed(const ListValue* args) {
288 // Be robust in the input parameters, but crash in a Debug build.
289 if (args->GetSize() != 2) {
295 bool allowed = false;
296 if (!args->GetString(0, &plugin) || !args->GetBoolean(1, &allowed)) {
300 Profile* profile = Profile::FromWebUI(web_ui());
301 profile->GetHostContentSettingsMap()->SetContentSetting(
302 ContentSettingsPattern::Wildcard(),
303 ContentSettingsPattern::Wildcard(),
304 CONTENT_SETTINGS_TYPE_PLUGINS,
306 allowed ? CONTENT_SETTING_ALLOW : CONTENT_SETTING_DEFAULT);
308 // Keep track of the whitelist separately, so that we can distinguish plug-ins
309 // whitelisted by the user from automatically whitelisted ones.
310 DictionaryPrefUpdate update(profile->GetPrefs(),
311 prefs::kContentSettingsPluginWhitelist);
312 update->SetBoolean(plugin, allowed);
315 void PluginsDOMHandler::Observe(int type,
316 const content::NotificationSource& source,
317 const content::NotificationDetails& details) {
318 DCHECK_EQ(chrome::NOTIFICATION_PLUGIN_ENABLE_STATUS_CHANGED, type);
322 void PluginsDOMHandler::LoadPlugins() {
323 if (weak_ptr_factory_.HasWeakPtrs())
326 PluginService::GetInstance()->GetPlugins(
327 base::Bind(&PluginsDOMHandler::PluginsLoaded,
328 weak_ptr_factory_.GetWeakPtr()));
331 void PluginsDOMHandler::PluginsLoaded(
332 const std::vector<WebPluginInfo>& plugins) {
333 Profile* profile = Profile::FromWebUI(web_ui());
334 PluginPrefs* plugin_prefs = PluginPrefs::GetForProfile(profile).get();
336 ContentSettingsPattern wildcard = ContentSettingsPattern::Wildcard();
338 PluginFinder* plugin_finder = PluginFinder::GetInstance();
339 // Group plug-ins by identifier. This is done to be able to display
340 // the plug-ins in UI in a grouped fashion.
342 for (size_t i = 0; i < plugins.size(); ++i) {
343 scoped_ptr<PluginMetadata> plugin(
344 plugin_finder->GetPluginMetadata(plugins[i]));
345 groups[plugin->identifier()].push_back(&plugins[i]);
348 // Construct DictionaryValues to return to UI.
349 ListValue* plugin_groups_data = new ListValue();
350 for (PluginGroups::const_iterator it = groups.begin();
351 it != groups.end(); ++it) {
352 const std::vector<const WebPluginInfo*>& group_plugins = it->second;
353 ListValue* plugin_files = new ListValue();
354 scoped_ptr<PluginMetadata> plugin_metadata(
355 plugin_finder->GetPluginMetadata(*group_plugins[0]));
356 string16 group_name = plugin_metadata->name();
357 std::string group_identifier = plugin_metadata->identifier();
358 bool group_enabled = false;
359 bool all_plugins_enabled_by_policy = true;
360 bool all_plugins_disabled_by_policy = true;
361 bool all_plugins_managed_by_policy = true;
362 const WebPluginInfo* active_plugin = NULL;
363 for (size_t j = 0; j < group_plugins.size(); ++j) {
364 const WebPluginInfo& group_plugin = *group_plugins[j];
366 DictionaryValue* plugin_file = new DictionaryValue();
367 plugin_file->SetString("name", group_plugin.name);
368 plugin_file->SetString("description", group_plugin.desc);
369 plugin_file->SetString("path", group_plugin.path.value());
370 plugin_file->SetString("version", group_plugin.version);
371 plugin_file->SetString("type", PluginTypeToString(group_plugin.type));
373 ListValue* mime_types = new ListValue();
374 const std::vector<content::WebPluginMimeType>& plugin_mime_types =
375 group_plugin.mime_types;
376 for (size_t k = 0; k < plugin_mime_types.size(); ++k) {
377 DictionaryValue* mime_type = new DictionaryValue();
378 mime_type->SetString("mimeType", plugin_mime_types[k].mime_type);
379 mime_type->SetString("description", plugin_mime_types[k].description);
381 ListValue* file_extensions = new ListValue();
382 const std::vector<std::string>& mime_file_extensions =
383 plugin_mime_types[k].file_extensions;
384 for (size_t l = 0; l < mime_file_extensions.size(); ++l)
385 file_extensions->Append(new StringValue(mime_file_extensions[l]));
386 mime_type->Set("fileExtensions", file_extensions);
388 mime_types->Append(mime_type);
390 plugin_file->Set("mimeTypes", mime_types);
392 bool plugin_enabled = plugin_prefs->IsPluginEnabled(group_plugin);
394 if (!active_plugin || (plugin_enabled && !group_enabled))
395 active_plugin = &group_plugin;
396 group_enabled = plugin_enabled || group_enabled;
398 std::string enabled_mode;
399 PluginPrefs::PolicyStatus plugin_status =
400 plugin_prefs->PolicyStatusForPlugin(group_plugin.name);
401 PluginPrefs::PolicyStatus group_status =
402 plugin_prefs->PolicyStatusForPlugin(group_name);
403 if (plugin_status == PluginPrefs::POLICY_ENABLED ||
404 group_status == PluginPrefs::POLICY_ENABLED) {
405 enabled_mode = "enabledByPolicy";
406 all_plugins_disabled_by_policy = false;
408 all_plugins_enabled_by_policy = false;
409 if (plugin_status == PluginPrefs::POLICY_DISABLED ||
410 group_status == PluginPrefs::POLICY_DISABLED) {
411 enabled_mode = "disabledByPolicy";
413 all_plugins_disabled_by_policy = false;
414 all_plugins_managed_by_policy = false;
415 if (plugin_enabled) {
416 enabled_mode = "enabledByUser";
418 enabled_mode = "disabledByUser";
422 plugin_file->SetString("enabledMode", enabled_mode);
424 plugin_files->Append(plugin_file);
426 DictionaryValue* group_data = new DictionaryValue();
428 group_data->Set("plugin_files", plugin_files);
429 group_data->SetString("name", group_name);
430 group_data->SetString("id", group_identifier);
431 group_data->SetString("description", active_plugin->desc);
432 group_data->SetString("version", active_plugin->version);
434 #if defined(ENABLE_PLUGIN_INSTALLATION)
435 bool out_of_date = plugin_metadata->GetSecurityStatus(*active_plugin) ==
436 PluginMetadata::SECURITY_STATUS_OUT_OF_DATE;
437 group_data->SetBoolean("critical", out_of_date);
438 group_data->SetString("update_url", plugin_metadata->plugin_url().spec());
441 std::string enabled_mode;
442 if (all_plugins_enabled_by_policy) {
443 enabled_mode = "enabledByPolicy";
444 } else if (all_plugins_disabled_by_policy) {
445 enabled_mode = "disabledByPolicy";
446 } else if (all_plugins_managed_by_policy) {
447 enabled_mode = "managedByPolicy";
448 } else if (group_enabled) {
449 enabled_mode = "enabledByUser";
451 enabled_mode = "disabledByUser";
453 group_data->SetString("enabledMode", enabled_mode);
455 bool always_allowed = false;
457 const DictionaryValue* whitelist = profile->GetPrefs()->GetDictionary(
458 prefs::kContentSettingsPluginWhitelist);
459 whitelist->GetBoolean(group_identifier, &always_allowed);
461 group_data->SetBoolean("alwaysAllowed", always_allowed);
463 plugin_groups_data->Append(group_data);
465 DictionaryValue results;
466 results.Set("plugins", plugin_groups_data);
467 web_ui()->CallJavascriptFunction("returnPluginsData", results);
472 ///////////////////////////////////////////////////////////////////////////////
476 ///////////////////////////////////////////////////////////////////////////////
478 PluginsUI::PluginsUI(content::WebUI* web_ui) : WebUIController(web_ui) {
479 web_ui->AddMessageHandler(new PluginsDOMHandler());
481 // Set up the chrome://plugins/ source.
482 Profile* profile = Profile::FromWebUI(web_ui);
483 content::WebUIDataSource::Add(profile, CreatePluginsUIHTMLSource());
487 base::RefCountedMemory* PluginsUI::GetFaviconResourceBytes(
488 ui::ScaleFactor scale_factor) {
489 return ResourceBundle::GetSharedInstance().
490 LoadDataResourceBytesForScale(IDR_PLUGINS_FAVICON, scale_factor);
494 void PluginsUI::RegisterProfilePrefs(
495 user_prefs::PrefRegistrySyncable* registry) {
496 registry->RegisterBooleanPref(
497 prefs::kPluginsShowDetails,
499 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
500 registry->RegisterDictionaryPref(
501 prefs::kContentSettingsPluginWhitelist,
502 user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);