plugins_ui.cc revision 7dbb3d5cf0c15f500944d211057644d6a2f37371
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.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
53using content::PluginService;
54using content::WebContents;
55using content::WebUIMessageHandler;
56using webkit::WebPluginInfo;
57
58namespace {
59
60// Callback function to process result of EnablePlugin method.
61void AssertPluginEnabled(bool did_enable) {
62  DCHECK(did_enable);
63}
64
65content::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
109string16 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>
137class 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<webkit::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
188PluginsDOMHandler::PluginsDOMHandler()
189    : weak_ptr_factory_(this) {
190}
191
192void 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
219void PluginsDOMHandler::HandleRequestPluginsData(const ListValue* args) {
220  LoadPlugins();
221}
222
223void 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
273void 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
282void PluginsDOMHandler::HandleGetShowDetails(const ListValue* args) {
283  base::FundamentalValue show_details(show_details_.GetValue());
284  web_ui()->CallJavascriptFunction("loadShowDetailsFromPrefs", show_details);
285}
286
287void 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
315void 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
322void 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
331void PluginsDOMHandler::PluginsLoaded(
332    const std::vector<webkit::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<webkit::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
478PluginsUI::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
487base::RefCountedMemory* PluginsUI::GetFaviconResourceBytes(
488      ui::ScaleFactor scale_factor) {
489  return ResourceBundle::GetSharedInstance().
490      LoadDataResourceBytesForScale(IDR_PLUGINS_FAVICON, scale_factor);
491}
492
493// static
494void 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