Home | History | Annotate | Download | only in webui
      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.
      4 
      5 #include "chrome/browser/ui/webui/plugins_ui.h"
      6 
      7 #include <algorithm>
      8 #include <string>
      9 #include <vector>
     10 
     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/strings/utf_string_conversions.h"
     22 #include "base/values.h"
     23 #include "chrome/browser/chrome_notification_types.h"
     24 #include "chrome/browser/content_settings/host_content_settings_map.h"
     25 #include "chrome/browser/plugins/plugin_finder.h"
     26 #include "chrome/browser/plugins/plugin_metadata.h"
     27 #include "chrome/browser/plugins/plugin_prefs.h"
     28 #include "chrome/browser/prefs/scoped_user_pref_update.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"
     48 
     49 #if defined(OS_CHROMEOS)
     50 #include "chrome/browser/ui/webui/chromeos/ui_account_tweaks.h"
     51 #endif
     52 
     53 using content::PluginService;
     54 using content::WebContents;
     55 using content::WebPluginInfo;
     56 using content::WebUIMessageHandler;
     57 
     58 namespace {
     59 
     60 // Callback function to process result of EnablePlugin method.
     61 void AssertPluginEnabled(bool did_enable) {
     62   DCHECK(did_enable);
     63 }
     64 
     65 content::WebUIDataSource* CreatePluginsUIHTMLSource() {
     66   content::WebUIDataSource* source =
     67       content::WebUIDataSource::Create(chrome::kChromeUIPluginsHost);
     68   source->SetUseJsonJSFormatV2();
     69 
     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);
     99 
    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);
    105 #endif
    106   return source;
    107 }
    108 
    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
    112   // a new type).
    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);
    122   }
    123   NOTREACHED();
    124   return string16();
    125 }
    126 
    127 ////////////////////////////////////////////////////////////////////////////////
    128 //
    129 // PluginsDOMHandler
    130 //
    131 ////////////////////////////////////////////////////////////////////////////////
    132 
    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 {
    139  public:
    140   explicit PluginsDOMHandler();
    141   virtual ~PluginsDOMHandler() {}
    142 
    143   // WebUIMessageHandler implementation.
    144   virtual void RegisterMessages() OVERRIDE;
    145 
    146   // Callback for the "requestPluginsData" message.
    147   void HandleRequestPluginsData(const ListValue* args);
    148 
    149   // Callback for the "enablePlugin" message.
    150   void HandleEnablePluginMessage(const ListValue* args);
    151 
    152   // Callback for the "saveShowDetailsToPrefs" message.
    153   void HandleSaveShowDetailsToPrefs(const ListValue* args);
    154 
    155   // Calback for the "getShowDetails" message.
    156   void HandleGetShowDetails(const ListValue* args);
    157 
    158   // Callback for the "setPluginAlwaysAllowed" message.
    159   void HandleSetPluginAlwaysAllowed(const ListValue* args);
    160 
    161   // content::NotificationObserver method overrides
    162   virtual void Observe(int type,
    163                        const content::NotificationSource& source,
    164                        const content::NotificationDetails& details) OVERRIDE;
    165 
    166  private:
    167   void LoadPlugins();
    168 
    169   // Called on the UI thread when the plugin information is ready.
    170   void PluginsLoaded(const std::vector<WebPluginInfo>& plugins);
    171 
    172   content::NotificationRegistrar registrar_;
    173 
    174   base::WeakPtrFactory<PluginsDOMHandler> weak_ptr_factory_;
    175 
    176   // Holds grouped plug-ins. The key is the group identifier and
    177   // the value is the list of plug-ins belonging to the group.
    178   typedef base::hash_map<std::string, std::vector<const WebPluginInfo*> >
    179       PluginGroups;
    180 
    181   // This pref guards the value whether about:plugins is in the details mode or
    182   // not.
    183   BooleanPrefMember show_details_;
    184 
    185   DISALLOW_COPY_AND_ASSIGN(PluginsDOMHandler);
    186 };
    187 
    188 PluginsDOMHandler::PluginsDOMHandler()
    189     : weak_ptr_factory_(this) {
    190 }
    191 
    192 void PluginsDOMHandler::RegisterMessages() {
    193   Profile* profile = Profile::FromWebUI(web_ui());
    194 
    195   PrefService* prefs = profile->GetPrefs();
    196   show_details_.Init(prefs::kPluginsShowDetails, prefs);
    197 
    198   registrar_.Add(this,
    199                  chrome::NOTIFICATION_PLUGIN_ENABLE_STATUS_CHANGED,
    200                  content::Source<Profile>(profile));
    201 
    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)));
    217 }
    218 
    219 void PluginsDOMHandler::HandleRequestPluginsData(const ListValue* args) {
    220   LoadPlugins();
    221 }
    222 
    223 void PluginsDOMHandler::HandleEnablePluginMessage(const ListValue* args) {
    224   Profile* profile = Profile::FromWebUI(web_ui());
    225 
    226   // Be robust in accepting badness since plug-ins display HTML (hence
    227   // JavaScript).
    228   if (args->GetSize() != 3) {
    229     NOTREACHED();
    230     return;
    231   }
    232 
    233   std::string enable_str;
    234   std::string is_group_str;
    235   if (!args->GetString(1, &enable_str) || !args->GetString(2, &is_group_str)) {
    236     NOTREACHED();
    237     return;
    238   }
    239   bool enable = enable_str == "true";
    240 
    241   PluginPrefs* plugin_prefs = PluginPrefs::GetForProfile(profile).get();
    242   if (is_group_str == "true") {
    243     string16 group_name;
    244     if (!args->GetString(0, &group_name)) {
    245       NOTREACHED();
    246       return;
    247     }
    248 
    249     plugin_prefs->EnablePluginGroup(enable, group_name);
    250     if (enable) {
    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);
    260     }
    261   } else {
    262     base::FilePath::StringType file_path;
    263     if (!args->GetString(0, &file_path)) {
    264       NOTREACHED();
    265       return;
    266     }
    267 
    268     plugin_prefs->EnablePlugin(enable, base::FilePath(file_path),
    269                                base::Bind(&AssertPluginEnabled));
    270   }
    271 }
    272 
    273 void PluginsDOMHandler::HandleSaveShowDetailsToPrefs(const ListValue* args) {
    274   std::string details_mode;
    275   if (!args->GetString(0, &details_mode)) {
    276     NOTREACHED();
    277     return;
    278   }
    279   show_details_.SetValue(details_mode == "true");
    280 }
    281 
    282 void PluginsDOMHandler::HandleGetShowDetails(const ListValue* args) {
    283   base::FundamentalValue show_details(show_details_.GetValue());
    284   web_ui()->CallJavascriptFunction("loadShowDetailsFromPrefs", show_details);
    285 }
    286 
    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) {
    290     NOTREACHED();
    291     return;
    292   }
    293 
    294   std::string plugin;
    295   bool allowed = false;
    296   if (!args->GetString(0, &plugin) || !args->GetBoolean(1, &allowed)) {
    297     NOTREACHED();
    298     return;
    299   }
    300   Profile* profile = Profile::FromWebUI(web_ui());
    301   profile->GetHostContentSettingsMap()->SetContentSetting(
    302       ContentSettingsPattern::Wildcard(),
    303       ContentSettingsPattern::Wildcard(),
    304       CONTENT_SETTINGS_TYPE_PLUGINS,
    305       plugin,
    306       allowed ? CONTENT_SETTING_ALLOW : CONTENT_SETTING_DEFAULT);
    307 
    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);
    313 }
    314 
    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);
    319   LoadPlugins();
    320 }
    321 
    322 void PluginsDOMHandler::LoadPlugins() {
    323   if (weak_ptr_factory_.HasWeakPtrs())
    324     return;
    325 
    326   PluginService::GetInstance()->GetPlugins(
    327       base::Bind(&PluginsDOMHandler::PluginsLoaded,
    328                  weak_ptr_factory_.GetWeakPtr()));
    329 }
    330 
    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();
    335 
    336   ContentSettingsPattern wildcard = ContentSettingsPattern::Wildcard();
    337 
    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.
    341   PluginGroups groups;
    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]);
    346   }
    347 
    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];
    365 
    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));
    372 
    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);
    380 
    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);
    387 
    388         mime_types->Append(mime_type);
    389       }
    390       plugin_file->Set("mimeTypes", mime_types);
    391 
    392       bool plugin_enabled = plugin_prefs->IsPluginEnabled(group_plugin);
    393 
    394       if (!active_plugin || (plugin_enabled && !group_enabled))
    395         active_plugin = &group_plugin;
    396       group_enabled = plugin_enabled || group_enabled;
    397 
    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;
    407       } else {
    408         all_plugins_enabled_by_policy = false;
    409         if (plugin_status == PluginPrefs::POLICY_DISABLED ||
    410             group_status == PluginPrefs::POLICY_DISABLED) {
    411           enabled_mode = "disabledByPolicy";
    412         } else {
    413           all_plugins_disabled_by_policy = false;
    414           all_plugins_managed_by_policy = false;
    415           if (plugin_enabled) {
    416             enabled_mode = "enabledByUser";
    417           } else {
    418             enabled_mode = "disabledByUser";
    419           }
    420         }
    421       }
    422       plugin_file->SetString("enabledMode", enabled_mode);
    423 
    424       plugin_files->Append(plugin_file);
    425     }
    426     DictionaryValue* group_data = new DictionaryValue();
    427 
    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);
    433 
    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());
    439 #endif
    440 
    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";
    450     } else {
    451       enabled_mode = "disabledByUser";
    452     }
    453     group_data->SetString("enabledMode", enabled_mode);
    454 
    455     bool always_allowed = false;
    456     if (group_enabled) {
    457       const DictionaryValue* whitelist = profile->GetPrefs()->GetDictionary(
    458           prefs::kContentSettingsPluginWhitelist);
    459       whitelist->GetBoolean(group_identifier, &always_allowed);
    460     }
    461     group_data->SetBoolean("alwaysAllowed", always_allowed);
    462 
    463     plugin_groups_data->Append(group_data);
    464   }
    465   DictionaryValue results;
    466   results.Set("plugins", plugin_groups_data);
    467   web_ui()->CallJavascriptFunction("returnPluginsData", results);
    468 }
    469 
    470 }  // namespace
    471 
    472 ///////////////////////////////////////////////////////////////////////////////
    473 //
    474 // PluginsUI
    475 //
    476 ///////////////////////////////////////////////////////////////////////////////
    477 
    478 PluginsUI::PluginsUI(content::WebUI* web_ui) : WebUIController(web_ui) {
    479   web_ui->AddMessageHandler(new PluginsDOMHandler());
    480 
    481   // Set up the chrome://plugins/ source.
    482   Profile* profile = Profile::FromWebUI(web_ui);
    483   content::WebUIDataSource::Add(profile, CreatePluginsUIHTMLSource());
    484 }
    485 
    486 // static
    487 base::RefCountedMemory* PluginsUI::GetFaviconResourceBytes(
    488       ui::ScaleFactor scale_factor) {
    489   return ResourceBundle::GetSharedInstance().
    490       LoadDataResourceBytesForScale(IDR_PLUGINS_FAVICON, scale_factor);
    491 }
    492 
    493 // static
    494 void PluginsUI::RegisterProfilePrefs(
    495     user_prefs::PrefRegistrySyncable* registry) {
    496   registry->RegisterBooleanPref(
    497       prefs::kPluginsShowDetails,
    498       false,
    499       user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
    500   registry->RegisterDictionaryPref(
    501       prefs::kContentSettingsPluginWhitelist,
    502       user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
    503 }
    504