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/files/file_path.h"
14 #include "base/memory/ref_counted_memory.h"
15 #include "base/memory/scoped_ptr.h"
16 #include "base/memory/singleton.h"
17 #include "base/memory/weak_ptr.h"
18 #include "base/message_loop/message_loop.h"
19 #include "base/path_service.h"
20 #include "base/prefs/pref_member.h"
21 #include "base/prefs/pref_service.h"
22 #include "base/prefs/scoped_user_pref_update.h"
23 #include "base/strings/utf_string_conversions.h"
24 #include "base/values.h"
25 #include "chrome/browser/chrome_notification_types.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 "chrome/grit/generated_resources.h"
37 #include "components/content_settings/core/browser/host_content_settings_map.h"
38 #include "components/pref_registry/pref_registry_syncable.h"
39 #include "content/public/browser/notification_source.h"
40 #include "content/public/browser/plugin_service.h"
41 #include "content/public/browser/web_contents.h"
42 #include "content/public/browser/web_ui.h"
43 #include "content/public/browser/web_ui_data_source.h"
44 #include "content/public/browser/web_ui_message_handler.h"
45 #include "content/public/common/content_constants.h"
46 #include "grit/browser_resources.h"
47 #include "grit/theme_resources.h"
48 #include "ui/base/l10n/l10n_util.h"
49 #include "ui/base/resource/resource_bundle.h"
51 #if defined(OS_CHROMEOS)
52 #include "chrome/browser/ui/webui/chromeos/ui_account_tweaks.h"
55 using content::PluginService
;
56 using content::WebContents
;
57 using content::WebPluginInfo
;
58 using content::WebUIMessageHandler
;
62 // Callback function to process result of EnablePlugin method.
63 void AssertPluginEnabled(bool did_enable
) {
67 content::WebUIDataSource
* CreatePluginsUIHTMLSource(Profile
* profile
) {
68 content::WebUIDataSource
* source
=
69 content::WebUIDataSource::Create(chrome::kChromeUIPluginsHost
);
71 source
->AddLocalizedString("pluginsTitle", IDS_PLUGINS_TITLE
);
72 source
->AddLocalizedString("pluginsDetailsModeLink",
73 IDS_PLUGINS_DETAILS_MODE_LINK
);
74 source
->AddLocalizedString("pluginsNoneInstalled",
75 IDS_PLUGINS_NONE_INSTALLED
);
76 source
->AddLocalizedString("pluginDisabled", IDS_PLUGINS_DISABLED_PLUGIN
);
77 source
->AddLocalizedString("pluginDisabledByPolicy",
78 IDS_PLUGINS_DISABLED_BY_POLICY_PLUGIN
);
79 source
->AddLocalizedString("pluginEnabledByPolicy",
80 IDS_PLUGINS_ENABLED_BY_POLICY_PLUGIN
);
81 source
->AddLocalizedString("pluginGroupManagedByPolicy",
82 IDS_PLUGINS_GROUP_MANAGED_BY_POLICY
);
83 source
->AddLocalizedString("pluginDownload", IDS_PLUGINS_DOWNLOAD
);
84 source
->AddLocalizedString("pluginName", IDS_PLUGINS_NAME
);
85 source
->AddLocalizedString("pluginVersion", IDS_PLUGINS_VERSION
);
86 source
->AddLocalizedString("pluginDescription", IDS_PLUGINS_DESCRIPTION
);
87 source
->AddLocalizedString("pluginPath", IDS_PLUGINS_PATH
);
88 source
->AddLocalizedString("pluginType", IDS_PLUGINS_TYPE
);
89 source
->AddLocalizedString("pluginMimeTypes", IDS_PLUGINS_MIME_TYPES
);
90 source
->AddLocalizedString("pluginMimeTypesMimeType",
91 IDS_PLUGINS_MIME_TYPES_MIME_TYPE
);
92 source
->AddLocalizedString("pluginMimeTypesDescription",
93 IDS_PLUGINS_MIME_TYPES_DESCRIPTION
);
94 source
->AddLocalizedString("pluginMimeTypesFileExtensions",
95 IDS_PLUGINS_MIME_TYPES_FILE_EXTENSIONS
);
96 source
->AddLocalizedString("disable", IDS_PLUGINS_DISABLE
);
97 source
->AddLocalizedString("enable", IDS_PLUGINS_ENABLE
);
98 source
->AddLocalizedString("alwaysAllowed", IDS_PLUGINS_ALWAYS_ALLOWED
);
99 source
->AddLocalizedString("noPlugins", IDS_PLUGINS_NO_PLUGINS
);
101 source
->SetJsonPath("strings.js");
102 source
->AddResourcePath("plugins.js", IDR_PLUGINS_JS
);
103 source
->SetDefaultResource(IDR_PLUGINS_HTML
);
104 #if defined(OS_CHROMEOS)
105 chromeos::AddAccountUITweaksLocalizedValues(source
, profile
);
110 base::string16
PluginTypeToString(int type
) {
111 // The type is stored as an |int|, but doing the switch on the right
112 // enumeration type gives us better build-time error checking (if someone adds
114 switch (static_cast<WebPluginInfo::PluginType
>(type
)) {
115 case WebPluginInfo::PLUGIN_TYPE_NPAPI
:
116 return l10n_util::GetStringUTF16(IDS_PLUGINS_NPAPI
);
117 case WebPluginInfo::PLUGIN_TYPE_PEPPER_IN_PROCESS
:
118 return l10n_util::GetStringUTF16(IDS_PLUGINS_PPAPI_IN_PROCESS
);
119 case WebPluginInfo::PLUGIN_TYPE_PEPPER_OUT_OF_PROCESS
:
120 return l10n_util::GetStringUTF16(IDS_PLUGINS_PPAPI_OUT_OF_PROCESS
);
121 case WebPluginInfo::PLUGIN_TYPE_PEPPER_UNSANDBOXED
:
122 return l10n_util::GetStringUTF16(IDS_PLUGINS_PPAPI_UNSANDBOXED
);
123 case WebPluginInfo::PLUGIN_TYPE_BROWSER_PLUGIN
:
124 return l10n_util::GetStringUTF16(IDS_PLUGINS_BROWSER_PLUGIN
);
127 return base::string16();
130 ////////////////////////////////////////////////////////////////////////////////
134 ////////////////////////////////////////////////////////////////////////////////
136 // The handler for Javascript messages for the chrome://plugins/ page.
137 // TODO(viettrungluu): Make plugin list updates notify, and then observe
138 // changes; maybe replumb plugin list through plugin service?
139 // <http://crbug.com/39101>
140 class PluginsDOMHandler
: public WebUIMessageHandler
,
141 public content::NotificationObserver
{
144 ~PluginsDOMHandler() override
{}
146 // WebUIMessageHandler implementation.
147 void RegisterMessages() override
;
149 // Callback for the "requestPluginsData" message.
150 void HandleRequestPluginsData(const base::ListValue
* args
);
152 // Callback for the "enablePlugin" message.
153 void HandleEnablePluginMessage(const base::ListValue
* args
);
155 // Callback for the "saveShowDetailsToPrefs" message.
156 void HandleSaveShowDetailsToPrefs(const base::ListValue
* args
);
158 // Calback for the "getShowDetails" message.
159 void HandleGetShowDetails(const base::ListValue
* args
);
161 // Callback for the "setPluginAlwaysAllowed" message.
162 void HandleSetPluginAlwaysAllowed(const base::ListValue
* args
);
164 // content::NotificationObserver method overrides
165 void Observe(int type
,
166 const content::NotificationSource
& source
,
167 const content::NotificationDetails
& details
) override
;
172 // Called on the UI thread when the plugin information is ready.
173 void PluginsLoaded(const std::vector
<WebPluginInfo
>& plugins
);
175 content::NotificationRegistrar registrar_
;
177 // Holds grouped plugins. The key is the group identifier and
178 // the value is the list of plugins belonging to the group.
179 typedef base::hash_map
<std::string
, std::vector
<const WebPluginInfo
*> >
182 // This pref guards the value whether about:plugins is in the details mode or
184 BooleanPrefMember show_details_
;
186 base::WeakPtrFactory
<PluginsDOMHandler
> weak_ptr_factory_
;
188 DISALLOW_COPY_AND_ASSIGN(PluginsDOMHandler
);
191 PluginsDOMHandler::PluginsDOMHandler()
192 : weak_ptr_factory_(this) {
195 void PluginsDOMHandler::RegisterMessages() {
196 Profile
* profile
= Profile::FromWebUI(web_ui());
198 PrefService
* prefs
= profile
->GetPrefs();
199 show_details_
.Init(prefs::kPluginsShowDetails
, prefs
);
202 chrome::NOTIFICATION_PLUGIN_ENABLE_STATUS_CHANGED
,
203 content::Source
<Profile
>(profile
));
205 web_ui()->RegisterMessageCallback("requestPluginsData",
206 base::Bind(&PluginsDOMHandler::HandleRequestPluginsData
,
207 base::Unretained(this)));
208 web_ui()->RegisterMessageCallback("enablePlugin",
209 base::Bind(&PluginsDOMHandler::HandleEnablePluginMessage
,
210 base::Unretained(this)));
211 web_ui()->RegisterMessageCallback("setPluginAlwaysAllowed",
212 base::Bind(&PluginsDOMHandler::HandleSetPluginAlwaysAllowed
,
213 base::Unretained(this)));
214 web_ui()->RegisterMessageCallback("saveShowDetailsToPrefs",
215 base::Bind(&PluginsDOMHandler::HandleSaveShowDetailsToPrefs
,
216 base::Unretained(this)));
217 web_ui()->RegisterMessageCallback("getShowDetails",
218 base::Bind(&PluginsDOMHandler::HandleGetShowDetails
,
219 base::Unretained(this)));
222 void PluginsDOMHandler::HandleRequestPluginsData(const base::ListValue
* args
) {
226 void PluginsDOMHandler::HandleEnablePluginMessage(const base::ListValue
* args
) {
227 Profile
* profile
= Profile::FromWebUI(web_ui());
229 // Be robust in accepting badness since plugins display HTML (hence
231 if (args
->GetSize() != 3) {
236 std::string enable_str
;
237 std::string is_group_str
;
238 if (!args
->GetString(1, &enable_str
) || !args
->GetString(2, &is_group_str
)) {
242 bool enable
= enable_str
== "true";
244 PluginPrefs
* plugin_prefs
= PluginPrefs::GetForProfile(profile
).get();
245 if (is_group_str
== "true") {
246 base::string16 group_name
;
247 if (!args
->GetString(0, &group_name
)) {
252 plugin_prefs
->EnablePluginGroup(enable
, group_name
);
254 // See http://crbug.com/50105 for background.
255 base::string16 adobereader
= base::ASCIIToUTF16(
256 PluginMetadata::kAdobeReaderGroupName
);
257 base::string16 internalpdf
=
258 base::ASCIIToUTF16(ChromeContentClient::kPDFPluginName
);
259 if (group_name
== adobereader
)
260 plugin_prefs
->EnablePluginGroup(false, internalpdf
);
261 else if (group_name
== internalpdf
)
262 plugin_prefs
->EnablePluginGroup(false, adobereader
);
265 base::FilePath::StringType file_path
;
266 if (!args
->GetString(0, &file_path
)) {
271 plugin_prefs
->EnablePlugin(enable
, base::FilePath(file_path
),
272 base::Bind(&AssertPluginEnabled
));
276 void PluginsDOMHandler::HandleSaveShowDetailsToPrefs(
277 const base::ListValue
* args
) {
278 std::string details_mode
;
279 if (!args
->GetString(0, &details_mode
)) {
283 show_details_
.SetValue(details_mode
== "true");
286 void PluginsDOMHandler::HandleGetShowDetails(const base::ListValue
* args
) {
287 base::FundamentalValue
show_details(show_details_
.GetValue());
288 web_ui()->CallJavascriptFunction("loadShowDetailsFromPrefs", show_details
);
291 void PluginsDOMHandler::HandleSetPluginAlwaysAllowed(
292 const base::ListValue
* args
) {
293 // Be robust in the input parameters, but crash in a Debug build.
294 if (args
->GetSize() != 2) {
300 bool allowed
= false;
301 if (!args
->GetString(0, &plugin
) || !args
->GetBoolean(1, &allowed
)) {
305 Profile
* profile
= Profile::FromWebUI(web_ui());
306 profile
->GetHostContentSettingsMap()->SetContentSetting(
307 ContentSettingsPattern::Wildcard(),
308 ContentSettingsPattern::Wildcard(),
309 CONTENT_SETTINGS_TYPE_PLUGINS
,
311 allowed
? CONTENT_SETTING_ALLOW
: CONTENT_SETTING_DEFAULT
);
313 // Keep track of the whitelist separately, so that we can distinguish plugins
314 // whitelisted by the user from automatically whitelisted ones.
315 DictionaryPrefUpdate
update(profile
->GetPrefs(),
316 prefs::kContentSettingsPluginWhitelist
);
317 update
->SetBoolean(plugin
, allowed
);
320 void PluginsDOMHandler::Observe(int type
,
321 const content::NotificationSource
& source
,
322 const content::NotificationDetails
& details
) {
323 DCHECK_EQ(chrome::NOTIFICATION_PLUGIN_ENABLE_STATUS_CHANGED
, type
);
327 void PluginsDOMHandler::LoadPlugins() {
328 if (weak_ptr_factory_
.HasWeakPtrs())
331 PluginService::GetInstance()->GetPlugins(
332 base::Bind(&PluginsDOMHandler::PluginsLoaded
,
333 weak_ptr_factory_
.GetWeakPtr()));
336 void PluginsDOMHandler::PluginsLoaded(
337 const std::vector
<WebPluginInfo
>& plugins
) {
338 Profile
* profile
= Profile::FromWebUI(web_ui());
339 PluginPrefs
* plugin_prefs
= PluginPrefs::GetForProfile(profile
).get();
341 ContentSettingsPattern wildcard
= ContentSettingsPattern::Wildcard();
343 PluginFinder
* plugin_finder
= PluginFinder::GetInstance();
344 // Group plugins by identifier. This is done to be able to display
345 // the plugins in UI in a grouped fashion.
347 for (size_t i
= 0; i
< plugins
.size(); ++i
) {
348 scoped_ptr
<PluginMetadata
> plugin(
349 plugin_finder
->GetPluginMetadata(plugins
[i
]));
350 groups
[plugin
->identifier()].push_back(&plugins
[i
]);
353 // Construct DictionaryValues to return to UI.
354 base::ListValue
* plugin_groups_data
= new base::ListValue();
355 for (PluginGroups::const_iterator it
= groups
.begin();
356 it
!= groups
.end(); ++it
) {
357 const std::vector
<const WebPluginInfo
*>& group_plugins
= it
->second
;
358 base::ListValue
* plugin_files
= new base::ListValue();
359 scoped_ptr
<PluginMetadata
> plugin_metadata(
360 plugin_finder
->GetPluginMetadata(*group_plugins
[0]));
361 base::string16 group_name
= plugin_metadata
->name();
362 std::string group_identifier
= plugin_metadata
->identifier();
363 bool group_enabled
= false;
364 bool all_plugins_enabled_by_policy
= true;
365 bool all_plugins_disabled_by_policy
= true;
366 bool all_plugins_managed_by_policy
= true;
367 const WebPluginInfo
* active_plugin
= NULL
;
368 for (size_t j
= 0; j
< group_plugins
.size(); ++j
) {
369 const WebPluginInfo
& group_plugin
= *group_plugins
[j
];
371 base::DictionaryValue
* plugin_file
= new base::DictionaryValue();
372 plugin_file
->SetString("name", group_plugin
.name
);
374 // If this plugin is Pepper Flash, and the plugin path is the same as the
375 // path for the Pepper Flash System plugin, then mark this plugin
376 // description as the system plugin to help the user disambiguate the
378 base::string16 desc
= group_plugin
.desc
;
379 if (group_plugin
.is_pepper_plugin() &&
380 group_plugin
.name
== base::ASCIIToUTF16(content::kFlashPluginName
)) {
381 base::FilePath system_path
;
382 PathService::Get(chrome::DIR_PEPPER_FLASH_SYSTEM_PLUGIN
, &system_path
);
383 if (group_plugin
.path
.DirName() == system_path
) {
384 #if defined(GOOGLE_CHROME_BUILD)
385 // Existing documentation for debugging Flash describe this plugin as
386 // "Debug" so preserve this nomenclature here.
387 desc
+= base::ASCIIToUTF16(" Debug");
389 // On Chromium, we can name it what it really is; the system plugin.
390 desc
+= base::ASCIIToUTF16(" System");
394 plugin_file
->SetString("description", desc
);
396 plugin_file
->SetString("path", group_plugin
.path
.value());
397 plugin_file
->SetString("version", group_plugin
.version
);
398 plugin_file
->SetString("type", PluginTypeToString(group_plugin
.type
));
400 base::ListValue
* mime_types
= new base::ListValue();
401 const std::vector
<content::WebPluginMimeType
>& plugin_mime_types
=
402 group_plugin
.mime_types
;
403 for (size_t k
= 0; k
< plugin_mime_types
.size(); ++k
) {
404 base::DictionaryValue
* mime_type
= new base::DictionaryValue();
405 mime_type
->SetString("mimeType", plugin_mime_types
[k
].mime_type
);
406 mime_type
->SetString("description", plugin_mime_types
[k
].description
);
408 base::ListValue
* file_extensions
= new base::ListValue();
409 const std::vector
<std::string
>& mime_file_extensions
=
410 plugin_mime_types
[k
].file_extensions
;
411 for (size_t l
= 0; l
< mime_file_extensions
.size(); ++l
) {
412 file_extensions
->Append(
413 new base::StringValue(mime_file_extensions
[l
]));
415 mime_type
->Set("fileExtensions", file_extensions
);
417 mime_types
->Append(mime_type
);
419 plugin_file
->Set("mimeTypes", mime_types
);
421 bool plugin_enabled
= plugin_prefs
->IsPluginEnabled(group_plugin
);
423 if (!active_plugin
|| (plugin_enabled
&& !group_enabled
))
424 active_plugin
= &group_plugin
;
425 group_enabled
= plugin_enabled
|| group_enabled
;
427 std::string enabled_mode
;
428 PluginPrefs::PolicyStatus plugin_status
=
429 plugin_prefs
->PolicyStatusForPlugin(group_plugin
.name
);
430 PluginPrefs::PolicyStatus group_status
=
431 plugin_prefs
->PolicyStatusForPlugin(group_name
);
432 if (plugin_status
== PluginPrefs::POLICY_ENABLED
||
433 group_status
== PluginPrefs::POLICY_ENABLED
) {
434 enabled_mode
= "enabledByPolicy";
435 all_plugins_disabled_by_policy
= false;
437 all_plugins_enabled_by_policy
= false;
438 if (plugin_status
== PluginPrefs::POLICY_DISABLED
||
439 group_status
== PluginPrefs::POLICY_DISABLED
) {
440 enabled_mode
= "disabledByPolicy";
442 all_plugins_disabled_by_policy
= false;
443 all_plugins_managed_by_policy
= false;
444 if (plugin_enabled
) {
445 enabled_mode
= "enabledByUser";
447 enabled_mode
= "disabledByUser";
451 plugin_file
->SetString("enabledMode", enabled_mode
);
453 plugin_files
->Append(plugin_file
);
455 base::DictionaryValue
* group_data
= new base::DictionaryValue();
457 group_data
->Set("plugin_files", plugin_files
);
458 group_data
->SetString("name", group_name
);
459 group_data
->SetString("id", group_identifier
);
460 group_data
->SetString("description", active_plugin
->desc
);
461 group_data
->SetString("version", active_plugin
->version
);
463 #if defined(ENABLE_PLUGIN_INSTALLATION)
464 bool out_of_date
= plugin_metadata
->GetSecurityStatus(*active_plugin
) ==
465 PluginMetadata::SECURITY_STATUS_OUT_OF_DATE
;
466 group_data
->SetBoolean("critical", out_of_date
);
467 group_data
->SetString("update_url", plugin_metadata
->plugin_url().spec());
470 std::string enabled_mode
;
471 if (all_plugins_enabled_by_policy
) {
472 enabled_mode
= "enabledByPolicy";
473 } else if (all_plugins_disabled_by_policy
) {
474 enabled_mode
= "disabledByPolicy";
475 } else if (all_plugins_managed_by_policy
) {
476 enabled_mode
= "managedByPolicy";
477 } else if (group_enabled
) {
478 enabled_mode
= "enabledByUser";
480 enabled_mode
= "disabledByUser";
482 group_data
->SetString("enabledMode", enabled_mode
);
484 bool always_allowed
= false;
486 const base::DictionaryValue
* whitelist
=
487 profile
->GetPrefs()->GetDictionary(
488 prefs::kContentSettingsPluginWhitelist
);
489 whitelist
->GetBoolean(group_identifier
, &always_allowed
);
491 group_data
->SetBoolean("alwaysAllowed", always_allowed
);
493 plugin_groups_data
->Append(group_data
);
495 base::DictionaryValue results
;
496 results
.Set("plugins", plugin_groups_data
);
497 web_ui()->CallJavascriptFunction("returnPluginsData", results
);
502 ///////////////////////////////////////////////////////////////////////////////
506 ///////////////////////////////////////////////////////////////////////////////
508 PluginsUI::PluginsUI(content::WebUI
* web_ui
) : WebUIController(web_ui
) {
509 web_ui
->AddMessageHandler(new PluginsDOMHandler());
511 // Set up the chrome://plugins/ source.
512 Profile
* profile
= Profile::FromWebUI(web_ui
);
513 content::WebUIDataSource::Add(profile
, CreatePluginsUIHTMLSource(profile
));
517 base::RefCountedMemory
* PluginsUI::GetFaviconResourceBytes(
518 ui::ScaleFactor scale_factor
) {
519 return ResourceBundle::GetSharedInstance().
520 LoadDataResourceBytesForScale(IDR_PLUGINS_FAVICON
, scale_factor
);
524 void PluginsUI::RegisterProfilePrefs(
525 user_prefs::PrefRegistrySyncable
* registry
) {
526 registry
->RegisterBooleanPref(
527 prefs::kPluginsShowDetails
,
529 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF
);
530 registry
->RegisterDictionaryPref(
531 prefs::kContentSettingsPluginWhitelist
,
532 user_prefs::PrefRegistrySyncable::SYNCABLE_PREF
);