| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/ui/webui/plugins_ui.h" |
| |
| #include <algorithm> |
| #include <string> |
| #include <vector> |
| |
| #include "base/bind.h" |
| #include "base/bind_helpers.h" |
| #include "base/memory/ref_counted_memory.h" |
| #include "base/memory/scoped_ptr.h" |
| #include "base/memory/singleton.h" |
| #include "base/memory/weak_ptr.h" |
| #include "base/message_loop/message_loop.h" |
| #include "base/path_service.h" |
| #include "base/prefs/pref_member.h" |
| #include "base/prefs/pref_service.h" |
| #include "base/prefs/scoped_user_pref_update.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/values.h" |
| #include "chrome/browser/chrome_notification_types.h" |
| #include "chrome/browser/content_settings/host_content_settings_map.h" |
| #include "chrome/browser/plugins/plugin_finder.h" |
| #include "chrome/browser/plugins/plugin_metadata.h" |
| #include "chrome/browser/plugins/plugin_prefs.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/ui/browser.h" |
| #include "chrome/browser/ui/browser_window.h" |
| #include "chrome/common/chrome_content_client.h" |
| #include "chrome/common/chrome_paths.h" |
| #include "chrome/common/pref_names.h" |
| #include "chrome/common/url_constants.h" |
| #include "components/user_prefs/pref_registry_syncable.h" |
| #include "content/public/browser/notification_source.h" |
| #include "content/public/browser/plugin_service.h" |
| #include "content/public/browser/web_contents.h" |
| #include "content/public/browser/web_ui.h" |
| #include "content/public/browser/web_ui_data_source.h" |
| #include "content/public/browser/web_ui_message_handler.h" |
| #include "grit/browser_resources.h" |
| #include "grit/generated_resources.h" |
| #include "grit/theme_resources.h" |
| #include "ui/base/l10n/l10n_util.h" |
| #include "ui/base/resource/resource_bundle.h" |
| |
| #if defined(OS_CHROMEOS) |
| #include "chrome/browser/ui/webui/chromeos/ui_account_tweaks.h" |
| #endif |
| |
| using content::PluginService; |
| using content::WebContents; |
| using content::WebPluginInfo; |
| using content::WebUIMessageHandler; |
| |
| namespace { |
| |
| // Callback function to process result of EnablePlugin method. |
| void AssertPluginEnabled(bool did_enable) { |
| DCHECK(did_enable); |
| } |
| |
| content::WebUIDataSource* CreatePluginsUIHTMLSource() { |
| content::WebUIDataSource* source = |
| content::WebUIDataSource::Create(chrome::kChromeUIPluginsHost); |
| source->SetUseJsonJSFormatV2(); |
| |
| source->AddLocalizedString("pluginsTitle", IDS_PLUGINS_TITLE); |
| source->AddLocalizedString("pluginsDetailsModeLink", |
| IDS_PLUGINS_DETAILS_MODE_LINK); |
| source->AddLocalizedString("pluginsNoneInstalled", |
| IDS_PLUGINS_NONE_INSTALLED); |
| source->AddLocalizedString("pluginDisabled", IDS_PLUGINS_DISABLED_PLUGIN); |
| source->AddLocalizedString("pluginDisabledByPolicy", |
| IDS_PLUGINS_DISABLED_BY_POLICY_PLUGIN); |
| source->AddLocalizedString("pluginEnabledByPolicy", |
| IDS_PLUGINS_ENABLED_BY_POLICY_PLUGIN); |
| source->AddLocalizedString("pluginGroupManagedByPolicy", |
| IDS_PLUGINS_GROUP_MANAGED_BY_POLICY); |
| source->AddLocalizedString("pluginDownload", IDS_PLUGINS_DOWNLOAD); |
| source->AddLocalizedString("pluginName", IDS_PLUGINS_NAME); |
| source->AddLocalizedString("pluginVersion", IDS_PLUGINS_VERSION); |
| source->AddLocalizedString("pluginDescription", IDS_PLUGINS_DESCRIPTION); |
| source->AddLocalizedString("pluginPath", IDS_PLUGINS_PATH); |
| source->AddLocalizedString("pluginType", IDS_PLUGINS_TYPE); |
| source->AddLocalizedString("pluginMimeTypes", IDS_PLUGINS_MIME_TYPES); |
| source->AddLocalizedString("pluginMimeTypesMimeType", |
| IDS_PLUGINS_MIME_TYPES_MIME_TYPE); |
| source->AddLocalizedString("pluginMimeTypesDescription", |
| IDS_PLUGINS_MIME_TYPES_DESCRIPTION); |
| source->AddLocalizedString("pluginMimeTypesFileExtensions", |
| IDS_PLUGINS_MIME_TYPES_FILE_EXTENSIONS); |
| source->AddLocalizedString("disable", IDS_PLUGINS_DISABLE); |
| source->AddLocalizedString("enable", IDS_PLUGINS_ENABLE); |
| source->AddLocalizedString("alwaysAllowed", IDS_PLUGINS_ALWAYS_ALLOWED); |
| source->AddLocalizedString("noPlugins", IDS_PLUGINS_NO_PLUGINS); |
| |
| source->SetJsonPath("strings.js"); |
| source->AddResourcePath("plugins.js", IDR_PLUGINS_JS); |
| source->SetDefaultResource(IDR_PLUGINS_HTML); |
| #if defined(OS_CHROMEOS) |
| chromeos::AddAccountUITweaksLocalizedValues(source); |
| #endif |
| return source; |
| } |
| |
| string16 PluginTypeToString(int type) { |
| // The type is stored as an |int|, but doing the switch on the right |
| // enumeration type gives us better build-time error checking (if someone adds |
| // a new type). |
| switch (static_cast<WebPluginInfo::PluginType>(type)) { |
| case WebPluginInfo::PLUGIN_TYPE_NPAPI: |
| return l10n_util::GetStringUTF16(IDS_PLUGINS_NPAPI); |
| case WebPluginInfo::PLUGIN_TYPE_PEPPER_IN_PROCESS: |
| return l10n_util::GetStringUTF16(IDS_PLUGINS_PPAPI_IN_PROCESS); |
| case WebPluginInfo::PLUGIN_TYPE_PEPPER_OUT_OF_PROCESS: |
| return l10n_util::GetStringUTF16(IDS_PLUGINS_PPAPI_OUT_OF_PROCESS); |
| case WebPluginInfo::PLUGIN_TYPE_PEPPER_UNSANDBOXED: |
| return l10n_util::GetStringUTF16(IDS_PLUGINS_PPAPI_UNSANDBOXED); |
| } |
| NOTREACHED(); |
| return string16(); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // |
| // PluginsDOMHandler |
| // |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| // The handler for Javascript messages for the chrome://plugins/ page. |
| // TODO(viettrungluu): Make plugin list updates notify, and then observe |
| // changes; maybe replumb plugin list through plugin service? |
| // <http://crbug.com/39101> |
| class PluginsDOMHandler : public WebUIMessageHandler, |
| public content::NotificationObserver { |
| public: |
| explicit PluginsDOMHandler(); |
| virtual ~PluginsDOMHandler() {} |
| |
| // WebUIMessageHandler implementation. |
| virtual void RegisterMessages() OVERRIDE; |
| |
| // Callback for the "requestPluginsData" message. |
| void HandleRequestPluginsData(const ListValue* args); |
| |
| // Callback for the "enablePlugin" message. |
| void HandleEnablePluginMessage(const ListValue* args); |
| |
| // Callback for the "saveShowDetailsToPrefs" message. |
| void HandleSaveShowDetailsToPrefs(const ListValue* args); |
| |
| // Calback for the "getShowDetails" message. |
| void HandleGetShowDetails(const ListValue* args); |
| |
| // Callback for the "setPluginAlwaysAllowed" message. |
| void HandleSetPluginAlwaysAllowed(const ListValue* args); |
| |
| // content::NotificationObserver method overrides |
| virtual void Observe(int type, |
| const content::NotificationSource& source, |
| const content::NotificationDetails& details) OVERRIDE; |
| |
| private: |
| void LoadPlugins(); |
| |
| // Called on the UI thread when the plugin information is ready. |
| void PluginsLoaded(const std::vector<WebPluginInfo>& plugins); |
| |
| content::NotificationRegistrar registrar_; |
| |
| // Holds grouped plug-ins. The key is the group identifier and |
| // the value is the list of plug-ins belonging to the group. |
| typedef base::hash_map<std::string, std::vector<const WebPluginInfo*> > |
| PluginGroups; |
| |
| // This pref guards the value whether about:plugins is in the details mode or |
| // not. |
| BooleanPrefMember show_details_; |
| |
| base::WeakPtrFactory<PluginsDOMHandler> weak_ptr_factory_; |
| |
| DISALLOW_COPY_AND_ASSIGN(PluginsDOMHandler); |
| }; |
| |
| PluginsDOMHandler::PluginsDOMHandler() |
| : weak_ptr_factory_(this) { |
| } |
| |
| void PluginsDOMHandler::RegisterMessages() { |
| Profile* profile = Profile::FromWebUI(web_ui()); |
| |
| PrefService* prefs = profile->GetPrefs(); |
| show_details_.Init(prefs::kPluginsShowDetails, prefs); |
| |
| registrar_.Add(this, |
| chrome::NOTIFICATION_PLUGIN_ENABLE_STATUS_CHANGED, |
| content::Source<Profile>(profile)); |
| |
| web_ui()->RegisterMessageCallback("requestPluginsData", |
| base::Bind(&PluginsDOMHandler::HandleRequestPluginsData, |
| base::Unretained(this))); |
| web_ui()->RegisterMessageCallback("enablePlugin", |
| base::Bind(&PluginsDOMHandler::HandleEnablePluginMessage, |
| base::Unretained(this))); |
| web_ui()->RegisterMessageCallback("setPluginAlwaysAllowed", |
| base::Bind(&PluginsDOMHandler::HandleSetPluginAlwaysAllowed, |
| base::Unretained(this))); |
| web_ui()->RegisterMessageCallback("saveShowDetailsToPrefs", |
| base::Bind(&PluginsDOMHandler::HandleSaveShowDetailsToPrefs, |
| base::Unretained(this))); |
| web_ui()->RegisterMessageCallback("getShowDetails", |
| base::Bind(&PluginsDOMHandler::HandleGetShowDetails, |
| base::Unretained(this))); |
| } |
| |
| void PluginsDOMHandler::HandleRequestPluginsData(const ListValue* args) { |
| LoadPlugins(); |
| } |
| |
| void PluginsDOMHandler::HandleEnablePluginMessage(const ListValue* args) { |
| Profile* profile = Profile::FromWebUI(web_ui()); |
| |
| // Be robust in accepting badness since plug-ins display HTML (hence |
| // JavaScript). |
| if (args->GetSize() != 3) { |
| NOTREACHED(); |
| return; |
| } |
| |
| std::string enable_str; |
| std::string is_group_str; |
| if (!args->GetString(1, &enable_str) || !args->GetString(2, &is_group_str)) { |
| NOTREACHED(); |
| return; |
| } |
| bool enable = enable_str == "true"; |
| |
| PluginPrefs* plugin_prefs = PluginPrefs::GetForProfile(profile).get(); |
| if (is_group_str == "true") { |
| string16 group_name; |
| if (!args->GetString(0, &group_name)) { |
| NOTREACHED(); |
| return; |
| } |
| |
| plugin_prefs->EnablePluginGroup(enable, group_name); |
| if (enable) { |
| // See http://crbug.com/50105 for background. |
| string16 adobereader = ASCIIToUTF16( |
| PluginMetadata::kAdobeReaderGroupName); |
| string16 internalpdf = |
| ASCIIToUTF16(chrome::ChromeContentClient::kPDFPluginName); |
| if (group_name == adobereader) |
| plugin_prefs->EnablePluginGroup(false, internalpdf); |
| else if (group_name == internalpdf) |
| plugin_prefs->EnablePluginGroup(false, adobereader); |
| } |
| } else { |
| base::FilePath::StringType file_path; |
| if (!args->GetString(0, &file_path)) { |
| NOTREACHED(); |
| return; |
| } |
| |
| plugin_prefs->EnablePlugin(enable, base::FilePath(file_path), |
| base::Bind(&AssertPluginEnabled)); |
| } |
| } |
| |
| void PluginsDOMHandler::HandleSaveShowDetailsToPrefs(const ListValue* args) { |
| std::string details_mode; |
| if (!args->GetString(0, &details_mode)) { |
| NOTREACHED(); |
| return; |
| } |
| show_details_.SetValue(details_mode == "true"); |
| } |
| |
| void PluginsDOMHandler::HandleGetShowDetails(const ListValue* args) { |
| base::FundamentalValue show_details(show_details_.GetValue()); |
| web_ui()->CallJavascriptFunction("loadShowDetailsFromPrefs", show_details); |
| } |
| |
| void PluginsDOMHandler::HandleSetPluginAlwaysAllowed(const ListValue* args) { |
| // Be robust in the input parameters, but crash in a Debug build. |
| if (args->GetSize() != 2) { |
| NOTREACHED(); |
| return; |
| } |
| |
| std::string plugin; |
| bool allowed = false; |
| if (!args->GetString(0, &plugin) || !args->GetBoolean(1, &allowed)) { |
| NOTREACHED(); |
| return; |
| } |
| Profile* profile = Profile::FromWebUI(web_ui()); |
| profile->GetHostContentSettingsMap()->SetContentSetting( |
| ContentSettingsPattern::Wildcard(), |
| ContentSettingsPattern::Wildcard(), |
| CONTENT_SETTINGS_TYPE_PLUGINS, |
| plugin, |
| allowed ? CONTENT_SETTING_ALLOW : CONTENT_SETTING_DEFAULT); |
| |
| // Keep track of the whitelist separately, so that we can distinguish plug-ins |
| // whitelisted by the user from automatically whitelisted ones. |
| DictionaryPrefUpdate update(profile->GetPrefs(), |
| prefs::kContentSettingsPluginWhitelist); |
| update->SetBoolean(plugin, allowed); |
| } |
| |
| void PluginsDOMHandler::Observe(int type, |
| const content::NotificationSource& source, |
| const content::NotificationDetails& details) { |
| DCHECK_EQ(chrome::NOTIFICATION_PLUGIN_ENABLE_STATUS_CHANGED, type); |
| LoadPlugins(); |
| } |
| |
| void PluginsDOMHandler::LoadPlugins() { |
| if (weak_ptr_factory_.HasWeakPtrs()) |
| return; |
| |
| PluginService::GetInstance()->GetPlugins( |
| base::Bind(&PluginsDOMHandler::PluginsLoaded, |
| weak_ptr_factory_.GetWeakPtr())); |
| } |
| |
| void PluginsDOMHandler::PluginsLoaded( |
| const std::vector<WebPluginInfo>& plugins) { |
| Profile* profile = Profile::FromWebUI(web_ui()); |
| PluginPrefs* plugin_prefs = PluginPrefs::GetForProfile(profile).get(); |
| |
| ContentSettingsPattern wildcard = ContentSettingsPattern::Wildcard(); |
| |
| PluginFinder* plugin_finder = PluginFinder::GetInstance(); |
| // Group plug-ins by identifier. This is done to be able to display |
| // the plug-ins in UI in a grouped fashion. |
| PluginGroups groups; |
| for (size_t i = 0; i < plugins.size(); ++i) { |
| scoped_ptr<PluginMetadata> plugin( |
| plugin_finder->GetPluginMetadata(plugins[i])); |
| groups[plugin->identifier()].push_back(&plugins[i]); |
| } |
| |
| // Construct DictionaryValues to return to UI. |
| ListValue* plugin_groups_data = new ListValue(); |
| for (PluginGroups::const_iterator it = groups.begin(); |
| it != groups.end(); ++it) { |
| const std::vector<const WebPluginInfo*>& group_plugins = it->second; |
| ListValue* plugin_files = new ListValue(); |
| scoped_ptr<PluginMetadata> plugin_metadata( |
| plugin_finder->GetPluginMetadata(*group_plugins[0])); |
| string16 group_name = plugin_metadata->name(); |
| std::string group_identifier = plugin_metadata->identifier(); |
| bool group_enabled = false; |
| bool all_plugins_enabled_by_policy = true; |
| bool all_plugins_disabled_by_policy = true; |
| bool all_plugins_managed_by_policy = true; |
| const WebPluginInfo* active_plugin = NULL; |
| for (size_t j = 0; j < group_plugins.size(); ++j) { |
| const WebPluginInfo& group_plugin = *group_plugins[j]; |
| |
| DictionaryValue* plugin_file = new DictionaryValue(); |
| plugin_file->SetString("name", group_plugin.name); |
| plugin_file->SetString("description", group_plugin.desc); |
| plugin_file->SetString("path", group_plugin.path.value()); |
| plugin_file->SetString("version", group_plugin.version); |
| plugin_file->SetString("type", PluginTypeToString(group_plugin.type)); |
| |
| ListValue* mime_types = new ListValue(); |
| const std::vector<content::WebPluginMimeType>& plugin_mime_types = |
| group_plugin.mime_types; |
| for (size_t k = 0; k < plugin_mime_types.size(); ++k) { |
| DictionaryValue* mime_type = new DictionaryValue(); |
| mime_type->SetString("mimeType", plugin_mime_types[k].mime_type); |
| mime_type->SetString("description", plugin_mime_types[k].description); |
| |
| ListValue* file_extensions = new ListValue(); |
| const std::vector<std::string>& mime_file_extensions = |
| plugin_mime_types[k].file_extensions; |
| for (size_t l = 0; l < mime_file_extensions.size(); ++l) |
| file_extensions->Append(new StringValue(mime_file_extensions[l])); |
| mime_type->Set("fileExtensions", file_extensions); |
| |
| mime_types->Append(mime_type); |
| } |
| plugin_file->Set("mimeTypes", mime_types); |
| |
| bool plugin_enabled = plugin_prefs->IsPluginEnabled(group_plugin); |
| |
| if (!active_plugin || (plugin_enabled && !group_enabled)) |
| active_plugin = &group_plugin; |
| group_enabled = plugin_enabled || group_enabled; |
| |
| std::string enabled_mode; |
| PluginPrefs::PolicyStatus plugin_status = |
| plugin_prefs->PolicyStatusForPlugin(group_plugin.name); |
| PluginPrefs::PolicyStatus group_status = |
| plugin_prefs->PolicyStatusForPlugin(group_name); |
| if (plugin_status == PluginPrefs::POLICY_ENABLED || |
| group_status == PluginPrefs::POLICY_ENABLED) { |
| enabled_mode = "enabledByPolicy"; |
| all_plugins_disabled_by_policy = false; |
| } else { |
| all_plugins_enabled_by_policy = false; |
| if (plugin_status == PluginPrefs::POLICY_DISABLED || |
| group_status == PluginPrefs::POLICY_DISABLED) { |
| enabled_mode = "disabledByPolicy"; |
| } else { |
| all_plugins_disabled_by_policy = false; |
| all_plugins_managed_by_policy = false; |
| if (plugin_enabled) { |
| enabled_mode = "enabledByUser"; |
| } else { |
| enabled_mode = "disabledByUser"; |
| } |
| } |
| } |
| plugin_file->SetString("enabledMode", enabled_mode); |
| |
| plugin_files->Append(plugin_file); |
| } |
| DictionaryValue* group_data = new DictionaryValue(); |
| |
| group_data->Set("plugin_files", plugin_files); |
| group_data->SetString("name", group_name); |
| group_data->SetString("id", group_identifier); |
| group_data->SetString("description", active_plugin->desc); |
| group_data->SetString("version", active_plugin->version); |
| |
| #if defined(ENABLE_PLUGIN_INSTALLATION) |
| bool out_of_date = plugin_metadata->GetSecurityStatus(*active_plugin) == |
| PluginMetadata::SECURITY_STATUS_OUT_OF_DATE; |
| group_data->SetBoolean("critical", out_of_date); |
| group_data->SetString("update_url", plugin_metadata->plugin_url().spec()); |
| #endif |
| |
| std::string enabled_mode; |
| if (all_plugins_enabled_by_policy) { |
| enabled_mode = "enabledByPolicy"; |
| } else if (all_plugins_disabled_by_policy) { |
| enabled_mode = "disabledByPolicy"; |
| } else if (all_plugins_managed_by_policy) { |
| enabled_mode = "managedByPolicy"; |
| } else if (group_enabled) { |
| enabled_mode = "enabledByUser"; |
| } else { |
| enabled_mode = "disabledByUser"; |
| } |
| group_data->SetString("enabledMode", enabled_mode); |
| |
| bool always_allowed = false; |
| if (group_enabled) { |
| const DictionaryValue* whitelist = profile->GetPrefs()->GetDictionary( |
| prefs::kContentSettingsPluginWhitelist); |
| whitelist->GetBoolean(group_identifier, &always_allowed); |
| } |
| group_data->SetBoolean("alwaysAllowed", always_allowed); |
| |
| plugin_groups_data->Append(group_data); |
| } |
| DictionaryValue results; |
| results.Set("plugins", plugin_groups_data); |
| web_ui()->CallJavascriptFunction("returnPluginsData", results); |
| } |
| |
| } // namespace |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| // |
| // PluginsUI |
| // |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| PluginsUI::PluginsUI(content::WebUI* web_ui) : WebUIController(web_ui) { |
| web_ui->AddMessageHandler(new PluginsDOMHandler()); |
| |
| // Set up the chrome://plugins/ source. |
| Profile* profile = Profile::FromWebUI(web_ui); |
| content::WebUIDataSource::Add(profile, CreatePluginsUIHTMLSource()); |
| } |
| |
| // static |
| base::RefCountedMemory* PluginsUI::GetFaviconResourceBytes( |
| ui::ScaleFactor scale_factor) { |
| return ResourceBundle::GetSharedInstance(). |
| LoadDataResourceBytesForScale(IDR_PLUGINS_FAVICON, scale_factor); |
| } |
| |
| // static |
| void PluginsUI::RegisterProfilePrefs( |
| user_prefs::PrefRegistrySyncable* registry) { |
| registry->RegisterBooleanPref( |
| prefs::kPluginsShowDetails, |
| false, |
| user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); |
| registry->RegisterDictionaryPref( |
| prefs::kContentSettingsPluginWhitelist, |
| user_prefs::PrefRegistrySyncable::SYNCABLE_PREF); |
| } |