Upstream version 11.40.277.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / webui / plugins_ui.cc
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/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"
50
51 #if defined(OS_CHROMEOS)
52 #include "chrome/browser/ui/webui/chromeos/ui_account_tweaks.h"
53 #endif
54
55 using content::PluginService;
56 using content::WebContents;
57 using content::WebPluginInfo;
58 using content::WebUIMessageHandler;
59
60 namespace {
61
62 // Callback function to process result of EnablePlugin method.
63 void AssertPluginEnabled(bool did_enable) {
64   DCHECK(did_enable);
65 }
66
67 content::WebUIDataSource* CreatePluginsUIHTMLSource(Profile* profile) {
68   content::WebUIDataSource* source =
69       content::WebUIDataSource::Create(chrome::kChromeUIPluginsHost);
70
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);
100
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);
106 #endif
107   return source;
108 }
109
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
113   // a new type).
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);
125   }
126   NOTREACHED();
127   return base::string16();
128 }
129
130 ////////////////////////////////////////////////////////////////////////////////
131 //
132 // PluginsDOMHandler
133 //
134 ////////////////////////////////////////////////////////////////////////////////
135
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 {
142  public:
143   PluginsDOMHandler();
144   ~PluginsDOMHandler() override {}
145
146   // WebUIMessageHandler implementation.
147   void RegisterMessages() override;
148
149   // Callback for the "requestPluginsData" message.
150   void HandleRequestPluginsData(const base::ListValue* args);
151
152   // Callback for the "enablePlugin" message.
153   void HandleEnablePluginMessage(const base::ListValue* args);
154
155   // Callback for the "saveShowDetailsToPrefs" message.
156   void HandleSaveShowDetailsToPrefs(const base::ListValue* args);
157
158   // Calback for the "getShowDetails" message.
159   void HandleGetShowDetails(const base::ListValue* args);
160
161   // Callback for the "setPluginAlwaysAllowed" message.
162   void HandleSetPluginAlwaysAllowed(const base::ListValue* args);
163
164   // content::NotificationObserver method overrides
165   void Observe(int type,
166                const content::NotificationSource& source,
167                const content::NotificationDetails& details) override;
168
169  private:
170   void LoadPlugins();
171
172   // Called on the UI thread when the plugin information is ready.
173   void PluginsLoaded(const std::vector<WebPluginInfo>& plugins);
174
175   content::NotificationRegistrar registrar_;
176
177   // Holds grouped plug-ins. The key is the group identifier and
178   // the value is the list of plug-ins belonging to the group.
179   typedef base::hash_map<std::string, std::vector<const WebPluginInfo*> >
180       PluginGroups;
181
182   // This pref guards the value whether about:plugins is in the details mode or
183   // not.
184   BooleanPrefMember show_details_;
185
186   base::WeakPtrFactory<PluginsDOMHandler> weak_ptr_factory_;
187
188   DISALLOW_COPY_AND_ASSIGN(PluginsDOMHandler);
189 };
190
191 PluginsDOMHandler::PluginsDOMHandler()
192     : weak_ptr_factory_(this) {
193 }
194
195 void PluginsDOMHandler::RegisterMessages() {
196   Profile* profile = Profile::FromWebUI(web_ui());
197
198   PrefService* prefs = profile->GetPrefs();
199   show_details_.Init(prefs::kPluginsShowDetails, prefs);
200
201   registrar_.Add(this,
202                  chrome::NOTIFICATION_PLUGIN_ENABLE_STATUS_CHANGED,
203                  content::Source<Profile>(profile));
204
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)));
220 }
221
222 void PluginsDOMHandler::HandleRequestPluginsData(const base::ListValue* args) {
223   LoadPlugins();
224 }
225
226 void PluginsDOMHandler::HandleEnablePluginMessage(const base::ListValue* args) {
227   Profile* profile = Profile::FromWebUI(web_ui());
228
229   // Be robust in accepting badness since plug-ins display HTML (hence
230   // JavaScript).
231   if (args->GetSize() != 3) {
232     NOTREACHED();
233     return;
234   }
235
236   std::string enable_str;
237   std::string is_group_str;
238   if (!args->GetString(1, &enable_str) || !args->GetString(2, &is_group_str)) {
239     NOTREACHED();
240     return;
241   }
242   bool enable = enable_str == "true";
243
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)) {
248       NOTREACHED();
249       return;
250     }
251
252     plugin_prefs->EnablePluginGroup(enable, group_name);
253     if (enable) {
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);
263     }
264   } else {
265     base::FilePath::StringType file_path;
266     if (!args->GetString(0, &file_path)) {
267       NOTREACHED();
268       return;
269     }
270
271     plugin_prefs->EnablePlugin(enable, base::FilePath(file_path),
272                                base::Bind(&AssertPluginEnabled));
273   }
274 }
275
276 void PluginsDOMHandler::HandleSaveShowDetailsToPrefs(
277     const base::ListValue* args) {
278   std::string details_mode;
279   if (!args->GetString(0, &details_mode)) {
280     NOTREACHED();
281     return;
282   }
283   show_details_.SetValue(details_mode == "true");
284 }
285
286 void PluginsDOMHandler::HandleGetShowDetails(const base::ListValue* args) {
287   base::FundamentalValue show_details(show_details_.GetValue());
288   web_ui()->CallJavascriptFunction("loadShowDetailsFromPrefs", show_details);
289 }
290
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) {
295     NOTREACHED();
296     return;
297   }
298
299   std::string plugin;
300   bool allowed = false;
301   if (!args->GetString(0, &plugin) || !args->GetBoolean(1, &allowed)) {
302     NOTREACHED();
303     return;
304   }
305   Profile* profile = Profile::FromWebUI(web_ui());
306   profile->GetHostContentSettingsMap()->SetContentSetting(
307       ContentSettingsPattern::Wildcard(),
308       ContentSettingsPattern::Wildcard(),
309       CONTENT_SETTINGS_TYPE_PLUGINS,
310       plugin,
311       allowed ? CONTENT_SETTING_ALLOW : CONTENT_SETTING_DEFAULT);
312
313   // Keep track of the whitelist separately, so that we can distinguish plug-ins
314   // whitelisted by the user from automatically whitelisted ones.
315   DictionaryPrefUpdate update(profile->GetPrefs(),
316                               prefs::kContentSettingsPluginWhitelist);
317   update->SetBoolean(plugin, allowed);
318 }
319
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);
324   LoadPlugins();
325 }
326
327 void PluginsDOMHandler::LoadPlugins() {
328   if (weak_ptr_factory_.HasWeakPtrs())
329     return;
330
331   PluginService::GetInstance()->GetPlugins(
332       base::Bind(&PluginsDOMHandler::PluginsLoaded,
333                  weak_ptr_factory_.GetWeakPtr()));
334 }
335
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();
340
341   ContentSettingsPattern wildcard = ContentSettingsPattern::Wildcard();
342
343   PluginFinder* plugin_finder = PluginFinder::GetInstance();
344   // Group plug-ins by identifier. This is done to be able to display
345   // the plug-ins in UI in a grouped fashion.
346   PluginGroups groups;
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]);
351   }
352
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];
370
371       base::DictionaryValue* plugin_file = new base::DictionaryValue();
372       plugin_file->SetString("name", group_plugin.name);
373
374       // If this plugin is Pepper Flash, and the plugin path is the same as the
375       // path for the Pepper Flash Debugger plugin, then mark this plugin
376       // description as the debugger plugin to help the user disambiguate the
377       // two plugins.
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 debug_path;
382         PathService::Get(chrome::DIR_PEPPER_FLASH_DEBUGGER_PLUGIN, &debug_path);
383         if (group_plugin.path.DirName() == debug_path)
384           desc += base::ASCIIToUTF16(" Debug");
385       }
386       plugin_file->SetString("description", desc);
387
388       plugin_file->SetString("path", group_plugin.path.value());
389       plugin_file->SetString("version", group_plugin.version);
390       plugin_file->SetString("type", PluginTypeToString(group_plugin.type));
391
392       base::ListValue* mime_types = new base::ListValue();
393       const std::vector<content::WebPluginMimeType>& plugin_mime_types =
394           group_plugin.mime_types;
395       for (size_t k = 0; k < plugin_mime_types.size(); ++k) {
396         base::DictionaryValue* mime_type = new base::DictionaryValue();
397         mime_type->SetString("mimeType", plugin_mime_types[k].mime_type);
398         mime_type->SetString("description", plugin_mime_types[k].description);
399
400         base::ListValue* file_extensions = new base::ListValue();
401         const std::vector<std::string>& mime_file_extensions =
402             plugin_mime_types[k].file_extensions;
403         for (size_t l = 0; l < mime_file_extensions.size(); ++l) {
404           file_extensions->Append(
405               new base::StringValue(mime_file_extensions[l]));
406         }
407         mime_type->Set("fileExtensions", file_extensions);
408
409         mime_types->Append(mime_type);
410       }
411       plugin_file->Set("mimeTypes", mime_types);
412
413       bool plugin_enabled = plugin_prefs->IsPluginEnabled(group_plugin);
414
415       if (!active_plugin || (plugin_enabled && !group_enabled))
416         active_plugin = &group_plugin;
417       group_enabled = plugin_enabled || group_enabled;
418
419       std::string enabled_mode;
420       PluginPrefs::PolicyStatus plugin_status =
421           plugin_prefs->PolicyStatusForPlugin(group_plugin.name);
422       PluginPrefs::PolicyStatus group_status =
423           plugin_prefs->PolicyStatusForPlugin(group_name);
424       if (plugin_status == PluginPrefs::POLICY_ENABLED ||
425           group_status == PluginPrefs::POLICY_ENABLED) {
426         enabled_mode = "enabledByPolicy";
427         all_plugins_disabled_by_policy = false;
428       } else {
429         all_plugins_enabled_by_policy = false;
430         if (plugin_status == PluginPrefs::POLICY_DISABLED ||
431             group_status == PluginPrefs::POLICY_DISABLED) {
432           enabled_mode = "disabledByPolicy";
433         } else {
434           all_plugins_disabled_by_policy = false;
435           all_plugins_managed_by_policy = false;
436           if (plugin_enabled) {
437             enabled_mode = "enabledByUser";
438           } else {
439             enabled_mode = "disabledByUser";
440           }
441         }
442       }
443       plugin_file->SetString("enabledMode", enabled_mode);
444
445       plugin_files->Append(plugin_file);
446     }
447     base::DictionaryValue* group_data = new base::DictionaryValue();
448
449     group_data->Set("plugin_files", plugin_files);
450     group_data->SetString("name", group_name);
451     group_data->SetString("id", group_identifier);
452     group_data->SetString("description", active_plugin->desc);
453     group_data->SetString("version", active_plugin->version);
454
455 #if defined(ENABLE_PLUGIN_INSTALLATION)
456     bool out_of_date = plugin_metadata->GetSecurityStatus(*active_plugin) ==
457         PluginMetadata::SECURITY_STATUS_OUT_OF_DATE;
458     group_data->SetBoolean("critical", out_of_date);
459     group_data->SetString("update_url", plugin_metadata->plugin_url().spec());
460 #endif
461
462     std::string enabled_mode;
463     if (all_plugins_enabled_by_policy) {
464       enabled_mode = "enabledByPolicy";
465     } else if (all_plugins_disabled_by_policy) {
466       enabled_mode = "disabledByPolicy";
467     } else if (all_plugins_managed_by_policy) {
468       enabled_mode = "managedByPolicy";
469     } else if (group_enabled) {
470       enabled_mode = "enabledByUser";
471     } else {
472       enabled_mode = "disabledByUser";
473     }
474     group_data->SetString("enabledMode", enabled_mode);
475
476     bool always_allowed = false;
477     if (group_enabled) {
478       const base::DictionaryValue* whitelist =
479           profile->GetPrefs()->GetDictionary(
480               prefs::kContentSettingsPluginWhitelist);
481       whitelist->GetBoolean(group_identifier, &always_allowed);
482     }
483     group_data->SetBoolean("alwaysAllowed", always_allowed);
484
485     plugin_groups_data->Append(group_data);
486   }
487   base::DictionaryValue results;
488   results.Set("plugins", plugin_groups_data);
489   web_ui()->CallJavascriptFunction("returnPluginsData", results);
490 }
491
492 }  // namespace
493
494 ///////////////////////////////////////////////////////////////////////////////
495 //
496 // PluginsUI
497 //
498 ///////////////////////////////////////////////////////////////////////////////
499
500 PluginsUI::PluginsUI(content::WebUI* web_ui) : WebUIController(web_ui) {
501   web_ui->AddMessageHandler(new PluginsDOMHandler());
502
503   // Set up the chrome://plugins/ source.
504   Profile* profile = Profile::FromWebUI(web_ui);
505   content::WebUIDataSource::Add(profile, CreatePluginsUIHTMLSource(profile));
506 }
507
508 // static
509 base::RefCountedMemory* PluginsUI::GetFaviconResourceBytes(
510       ui::ScaleFactor scale_factor) {
511   return ResourceBundle::GetSharedInstance().
512       LoadDataResourceBytesForScale(IDR_PLUGINS_FAVICON, scale_factor);
513 }
514
515 // static
516 void PluginsUI::RegisterProfilePrefs(
517     user_prefs::PrefRegistrySyncable* registry) {
518   registry->RegisterBooleanPref(
519       prefs::kPluginsShowDetails,
520       false,
521       user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
522   registry->RegisterDictionaryPref(
523       prefs::kContentSettingsPluginWhitelist,
524       user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
525 }