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/content_settings/host_content_settings_map.h" 27 #include "chrome/browser/plugins/plugin_finder.h" 28 #include "chrome/browser/plugins/plugin_metadata.h" 29 #include "chrome/browser/plugins/plugin_prefs.h" 30 #include "chrome/browser/profiles/profile.h" 31 #include "chrome/browser/ui/browser.h" 32 #include "chrome/browser/ui/browser_window.h" 33 #include "chrome/common/chrome_content_client.h" 34 #include "chrome/common/chrome_paths.h" 35 #include "chrome/common/pref_names.h" 36 #include "chrome/common/url_constants.h" 37 #include "chrome/grit/generated_resources.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 source->SetUseJsonJSFormatV2(); 71 72 source->AddLocalizedString("pluginsTitle", IDS_PLUGINS_TITLE); 73 source->AddLocalizedString("pluginsDetailsModeLink", 74 IDS_PLUGINS_DETAILS_MODE_LINK); 75 source->AddLocalizedString("pluginsNoneInstalled", 76 IDS_PLUGINS_NONE_INSTALLED); 77 source->AddLocalizedString("pluginDisabled", IDS_PLUGINS_DISABLED_PLUGIN); 78 source->AddLocalizedString("pluginDisabledByPolicy", 79 IDS_PLUGINS_DISABLED_BY_POLICY_PLUGIN); 80 source->AddLocalizedString("pluginEnabledByPolicy", 81 IDS_PLUGINS_ENABLED_BY_POLICY_PLUGIN); 82 source->AddLocalizedString("pluginGroupManagedByPolicy", 83 IDS_PLUGINS_GROUP_MANAGED_BY_POLICY); 84 source->AddLocalizedString("pluginDownload", IDS_PLUGINS_DOWNLOAD); 85 source->AddLocalizedString("pluginName", IDS_PLUGINS_NAME); 86 source->AddLocalizedString("pluginVersion", IDS_PLUGINS_VERSION); 87 source->AddLocalizedString("pluginDescription", IDS_PLUGINS_DESCRIPTION); 88 source->AddLocalizedString("pluginPath", IDS_PLUGINS_PATH); 89 source->AddLocalizedString("pluginType", IDS_PLUGINS_TYPE); 90 source->AddLocalizedString("pluginMimeTypes", IDS_PLUGINS_MIME_TYPES); 91 source->AddLocalizedString("pluginMimeTypesMimeType", 92 IDS_PLUGINS_MIME_TYPES_MIME_TYPE); 93 source->AddLocalizedString("pluginMimeTypesDescription", 94 IDS_PLUGINS_MIME_TYPES_DESCRIPTION); 95 source->AddLocalizedString("pluginMimeTypesFileExtensions", 96 IDS_PLUGINS_MIME_TYPES_FILE_EXTENSIONS); 97 source->AddLocalizedString("disable", IDS_PLUGINS_DISABLE); 98 source->AddLocalizedString("enable", IDS_PLUGINS_ENABLE); 99 source->AddLocalizedString("alwaysAllowed", IDS_PLUGINS_ALWAYS_ALLOWED); 100 source->AddLocalizedString("noPlugins", IDS_PLUGINS_NO_PLUGINS); 101 102 source->SetJsonPath("strings.js"); 103 source->AddResourcePath("plugins.js", IDR_PLUGINS_JS); 104 source->SetDefaultResource(IDR_PLUGINS_HTML); 105 #if defined(OS_CHROMEOS) 106 chromeos::AddAccountUITweaksLocalizedValues(source, profile); 107 #endif 108 return source; 109 } 110 111 base::string16 PluginTypeToString(int type) { 112 // The type is stored as an |int|, but doing the switch on the right 113 // enumeration type gives us better build-time error checking (if someone adds 114 // a new type). 115 switch (static_cast<WebPluginInfo::PluginType>(type)) { 116 case WebPluginInfo::PLUGIN_TYPE_NPAPI: 117 return l10n_util::GetStringUTF16(IDS_PLUGINS_NPAPI); 118 case WebPluginInfo::PLUGIN_TYPE_PEPPER_IN_PROCESS: 119 return l10n_util::GetStringUTF16(IDS_PLUGINS_PPAPI_IN_PROCESS); 120 case WebPluginInfo::PLUGIN_TYPE_PEPPER_OUT_OF_PROCESS: 121 return l10n_util::GetStringUTF16(IDS_PLUGINS_PPAPI_OUT_OF_PROCESS); 122 case WebPluginInfo::PLUGIN_TYPE_PEPPER_UNSANDBOXED: 123 return l10n_util::GetStringUTF16(IDS_PLUGINS_PPAPI_UNSANDBOXED); 124 case WebPluginInfo::PLUGIN_TYPE_BROWSER_PLUGIN: 125 return l10n_util::GetStringUTF16(IDS_PLUGINS_BROWSER_PLUGIN); 126 } 127 NOTREACHED(); 128 return base::string16(); 129 } 130 131 //////////////////////////////////////////////////////////////////////////////// 132 // 133 // PluginsDOMHandler 134 // 135 //////////////////////////////////////////////////////////////////////////////// 136 137 // The handler for Javascript messages for the chrome://plugins/ page. 138 // TODO(viettrungluu): Make plugin list updates notify, and then observe 139 // changes; maybe replumb plugin list through plugin service? 140 // <http://crbug.com/39101> 141 class PluginsDOMHandler : public WebUIMessageHandler, 142 public content::NotificationObserver { 143 public: 144 PluginsDOMHandler(); 145 virtual ~PluginsDOMHandler() {} 146 147 // WebUIMessageHandler implementation. 148 virtual void RegisterMessages() OVERRIDE; 149 150 // Callback for the "requestPluginsData" message. 151 void HandleRequestPluginsData(const base::ListValue* args); 152 153 // Callback for the "enablePlugin" message. 154 void HandleEnablePluginMessage(const base::ListValue* args); 155 156 // Callback for the "saveShowDetailsToPrefs" message. 157 void HandleSaveShowDetailsToPrefs(const base::ListValue* args); 158 159 // Calback for the "getShowDetails" message. 160 void HandleGetShowDetails(const base::ListValue* args); 161 162 // Callback for the "setPluginAlwaysAllowed" message. 163 void HandleSetPluginAlwaysAllowed(const base::ListValue* args); 164 165 // content::NotificationObserver method overrides 166 virtual void Observe(int type, 167 const content::NotificationSource& source, 168 const content::NotificationDetails& details) OVERRIDE; 169 170 private: 171 void LoadPlugins(); 172 173 // Called on the UI thread when the plugin information is ready. 174 void PluginsLoaded(const std::vector<WebPluginInfo>& plugins); 175 176 content::NotificationRegistrar registrar_; 177 178 // Holds grouped plug-ins. The key is the group identifier and 179 // the value is the list of plug-ins belonging to the group. 180 typedef base::hash_map<std::string, std::vector<const WebPluginInfo*> > 181 PluginGroups; 182 183 // This pref guards the value whether about:plugins is in the details mode or 184 // not. 185 BooleanPrefMember show_details_; 186 187 base::WeakPtrFactory<PluginsDOMHandler> weak_ptr_factory_; 188 189 DISALLOW_COPY_AND_ASSIGN(PluginsDOMHandler); 190 }; 191 192 PluginsDOMHandler::PluginsDOMHandler() 193 : weak_ptr_factory_(this) { 194 } 195 196 void PluginsDOMHandler::RegisterMessages() { 197 Profile* profile = Profile::FromWebUI(web_ui()); 198 199 PrefService* prefs = profile->GetPrefs(); 200 show_details_.Init(prefs::kPluginsShowDetails, prefs); 201 202 registrar_.Add(this, 203 chrome::NOTIFICATION_PLUGIN_ENABLE_STATUS_CHANGED, 204 content::Source<Profile>(profile)); 205 206 web_ui()->RegisterMessageCallback("requestPluginsData", 207 base::Bind(&PluginsDOMHandler::HandleRequestPluginsData, 208 base::Unretained(this))); 209 web_ui()->RegisterMessageCallback("enablePlugin", 210 base::Bind(&PluginsDOMHandler::HandleEnablePluginMessage, 211 base::Unretained(this))); 212 web_ui()->RegisterMessageCallback("setPluginAlwaysAllowed", 213 base::Bind(&PluginsDOMHandler::HandleSetPluginAlwaysAllowed, 214 base::Unretained(this))); 215 web_ui()->RegisterMessageCallback("saveShowDetailsToPrefs", 216 base::Bind(&PluginsDOMHandler::HandleSaveShowDetailsToPrefs, 217 base::Unretained(this))); 218 web_ui()->RegisterMessageCallback("getShowDetails", 219 base::Bind(&PluginsDOMHandler::HandleGetShowDetails, 220 base::Unretained(this))); 221 } 222 223 void PluginsDOMHandler::HandleRequestPluginsData(const base::ListValue* args) { 224 LoadPlugins(); 225 } 226 227 void PluginsDOMHandler::HandleEnablePluginMessage(const base::ListValue* args) { 228 Profile* profile = Profile::FromWebUI(web_ui()); 229 230 // Be robust in accepting badness since plug-ins display HTML (hence 231 // JavaScript). 232 if (args->GetSize() != 3) { 233 NOTREACHED(); 234 return; 235 } 236 237 std::string enable_str; 238 std::string is_group_str; 239 if (!args->GetString(1, &enable_str) || !args->GetString(2, &is_group_str)) { 240 NOTREACHED(); 241 return; 242 } 243 bool enable = enable_str == "true"; 244 245 PluginPrefs* plugin_prefs = PluginPrefs::GetForProfile(profile).get(); 246 if (is_group_str == "true") { 247 base::string16 group_name; 248 if (!args->GetString(0, &group_name)) { 249 NOTREACHED(); 250 return; 251 } 252 253 plugin_prefs->EnablePluginGroup(enable, group_name); 254 if (enable) { 255 // See http://crbug.com/50105 for background. 256 base::string16 adobereader = base::ASCIIToUTF16( 257 PluginMetadata::kAdobeReaderGroupName); 258 base::string16 internalpdf = 259 base::ASCIIToUTF16(ChromeContentClient::kPDFPluginName); 260 if (group_name == adobereader) 261 plugin_prefs->EnablePluginGroup(false, internalpdf); 262 else if (group_name == internalpdf) 263 plugin_prefs->EnablePluginGroup(false, adobereader); 264 } 265 } else { 266 base::FilePath::StringType file_path; 267 if (!args->GetString(0, &file_path)) { 268 NOTREACHED(); 269 return; 270 } 271 272 plugin_prefs->EnablePlugin(enable, base::FilePath(file_path), 273 base::Bind(&AssertPluginEnabled)); 274 } 275 } 276 277 void PluginsDOMHandler::HandleSaveShowDetailsToPrefs( 278 const base::ListValue* args) { 279 std::string details_mode; 280 if (!args->GetString(0, &details_mode)) { 281 NOTREACHED(); 282 return; 283 } 284 show_details_.SetValue(details_mode == "true"); 285 } 286 287 void PluginsDOMHandler::HandleGetShowDetails(const base::ListValue* args) { 288 base::FundamentalValue show_details(show_details_.GetValue()); 289 web_ui()->CallJavascriptFunction("loadShowDetailsFromPrefs", show_details); 290 } 291 292 void PluginsDOMHandler::HandleSetPluginAlwaysAllowed( 293 const base::ListValue* args) { 294 // Be robust in the input parameters, but crash in a Debug build. 295 if (args->GetSize() != 2) { 296 NOTREACHED(); 297 return; 298 } 299 300 std::string plugin; 301 bool allowed = false; 302 if (!args->GetString(0, &plugin) || !args->GetBoolean(1, &allowed)) { 303 NOTREACHED(); 304 return; 305 } 306 Profile* profile = Profile::FromWebUI(web_ui()); 307 profile->GetHostContentSettingsMap()->SetContentSetting( 308 ContentSettingsPattern::Wildcard(), 309 ContentSettingsPattern::Wildcard(), 310 CONTENT_SETTINGS_TYPE_PLUGINS, 311 plugin, 312 allowed ? CONTENT_SETTING_ALLOW : CONTENT_SETTING_DEFAULT); 313 314 // Keep track of the whitelist separately, so that we can distinguish plug-ins 315 // whitelisted by the user from automatically whitelisted ones. 316 DictionaryPrefUpdate update(profile->GetPrefs(), 317 prefs::kContentSettingsPluginWhitelist); 318 update->SetBoolean(plugin, allowed); 319 } 320 321 void PluginsDOMHandler::Observe(int type, 322 const content::NotificationSource& source, 323 const content::NotificationDetails& details) { 324 DCHECK_EQ(chrome::NOTIFICATION_PLUGIN_ENABLE_STATUS_CHANGED, type); 325 LoadPlugins(); 326 } 327 328 void PluginsDOMHandler::LoadPlugins() { 329 if (weak_ptr_factory_.HasWeakPtrs()) 330 return; 331 332 PluginService::GetInstance()->GetPlugins( 333 base::Bind(&PluginsDOMHandler::PluginsLoaded, 334 weak_ptr_factory_.GetWeakPtr())); 335 } 336 337 void PluginsDOMHandler::PluginsLoaded( 338 const std::vector<WebPluginInfo>& plugins) { 339 Profile* profile = Profile::FromWebUI(web_ui()); 340 PluginPrefs* plugin_prefs = PluginPrefs::GetForProfile(profile).get(); 341 342 ContentSettingsPattern wildcard = ContentSettingsPattern::Wildcard(); 343 344 PluginFinder* plugin_finder = PluginFinder::GetInstance(); 345 // Group plug-ins by identifier. This is done to be able to display 346 // the plug-ins in UI in a grouped fashion. 347 PluginGroups groups; 348 for (size_t i = 0; i < plugins.size(); ++i) { 349 scoped_ptr<PluginMetadata> plugin( 350 plugin_finder->GetPluginMetadata(plugins[i])); 351 groups[plugin->identifier()].push_back(&plugins[i]); 352 } 353 354 // Construct DictionaryValues to return to UI. 355 base::ListValue* plugin_groups_data = new base::ListValue(); 356 for (PluginGroups::const_iterator it = groups.begin(); 357 it != groups.end(); ++it) { 358 const std::vector<const WebPluginInfo*>& group_plugins = it->second; 359 base::ListValue* plugin_files = new base::ListValue(); 360 scoped_ptr<PluginMetadata> plugin_metadata( 361 plugin_finder->GetPluginMetadata(*group_plugins[0])); 362 base::string16 group_name = plugin_metadata->name(); 363 std::string group_identifier = plugin_metadata->identifier(); 364 bool group_enabled = false; 365 bool all_plugins_enabled_by_policy = true; 366 bool all_plugins_disabled_by_policy = true; 367 bool all_plugins_managed_by_policy = true; 368 const WebPluginInfo* active_plugin = NULL; 369 for (size_t j = 0; j < group_plugins.size(); ++j) { 370 const WebPluginInfo& group_plugin = *group_plugins[j]; 371 372 base::DictionaryValue* plugin_file = new base::DictionaryValue(); 373 plugin_file->SetString("name", group_plugin.name); 374 375 // If this plugin is Pepper Flash, and the plugin path is the same as the 376 // path for the Pepper Flash Debugger plugin, then mark this plugin 377 // description as the debugger plugin to help the user disambiguate the 378 // two plugins. 379 base::string16 desc = group_plugin.desc; 380 if (group_plugin.is_pepper_plugin() && 381 group_plugin.name == base::ASCIIToUTF16(content::kFlashPluginName)) { 382 base::FilePath debug_path; 383 PathService::Get(chrome::DIR_PEPPER_FLASH_DEBUGGER_PLUGIN, &debug_path); 384 if (group_plugin.path.DirName() == debug_path) 385 desc += base::ASCIIToUTF16(" Debug"); 386 } 387 plugin_file->SetString("description", desc); 388 389 plugin_file->SetString("path", group_plugin.path.value()); 390 plugin_file->SetString("version", group_plugin.version); 391 plugin_file->SetString("type", PluginTypeToString(group_plugin.type)); 392 393 base::ListValue* mime_types = new base::ListValue(); 394 const std::vector<content::WebPluginMimeType>& plugin_mime_types = 395 group_plugin.mime_types; 396 for (size_t k = 0; k < plugin_mime_types.size(); ++k) { 397 base::DictionaryValue* mime_type = new base::DictionaryValue(); 398 mime_type->SetString("mimeType", plugin_mime_types[k].mime_type); 399 mime_type->SetString("description", plugin_mime_types[k].description); 400 401 base::ListValue* file_extensions = new base::ListValue(); 402 const std::vector<std::string>& mime_file_extensions = 403 plugin_mime_types[k].file_extensions; 404 for (size_t l = 0; l < mime_file_extensions.size(); ++l) { 405 file_extensions->Append( 406 new base::StringValue(mime_file_extensions[l])); 407 } 408 mime_type->Set("fileExtensions", file_extensions); 409 410 mime_types->Append(mime_type); 411 } 412 plugin_file->Set("mimeTypes", mime_types); 413 414 bool plugin_enabled = plugin_prefs->IsPluginEnabled(group_plugin); 415 416 if (!active_plugin || (plugin_enabled && !group_enabled)) 417 active_plugin = &group_plugin; 418 group_enabled = plugin_enabled || group_enabled; 419 420 std::string enabled_mode; 421 PluginPrefs::PolicyStatus plugin_status = 422 plugin_prefs->PolicyStatusForPlugin(group_plugin.name); 423 PluginPrefs::PolicyStatus group_status = 424 plugin_prefs->PolicyStatusForPlugin(group_name); 425 if (plugin_status == PluginPrefs::POLICY_ENABLED || 426 group_status == PluginPrefs::POLICY_ENABLED) { 427 enabled_mode = "enabledByPolicy"; 428 all_plugins_disabled_by_policy = false; 429 } else { 430 all_plugins_enabled_by_policy = false; 431 if (plugin_status == PluginPrefs::POLICY_DISABLED || 432 group_status == PluginPrefs::POLICY_DISABLED) { 433 enabled_mode = "disabledByPolicy"; 434 } else { 435 all_plugins_disabled_by_policy = false; 436 all_plugins_managed_by_policy = false; 437 if (plugin_enabled) { 438 enabled_mode = "enabledByUser"; 439 } else { 440 enabled_mode = "disabledByUser"; 441 } 442 } 443 } 444 plugin_file->SetString("enabledMode", enabled_mode); 445 446 plugin_files->Append(plugin_file); 447 } 448 base::DictionaryValue* group_data = new base::DictionaryValue(); 449 450 group_data->Set("plugin_files", plugin_files); 451 group_data->SetString("name", group_name); 452 group_data->SetString("id", group_identifier); 453 group_data->SetString("description", active_plugin->desc); 454 group_data->SetString("version", active_plugin->version); 455 456 #if defined(ENABLE_PLUGIN_INSTALLATION) 457 bool out_of_date = plugin_metadata->GetSecurityStatus(*active_plugin) == 458 PluginMetadata::SECURITY_STATUS_OUT_OF_DATE; 459 group_data->SetBoolean("critical", out_of_date); 460 group_data->SetString("update_url", plugin_metadata->plugin_url().spec()); 461 #endif 462 463 std::string enabled_mode; 464 if (all_plugins_enabled_by_policy) { 465 enabled_mode = "enabledByPolicy"; 466 } else if (all_plugins_disabled_by_policy) { 467 enabled_mode = "disabledByPolicy"; 468 } else if (all_plugins_managed_by_policy) { 469 enabled_mode = "managedByPolicy"; 470 } else if (group_enabled) { 471 enabled_mode = "enabledByUser"; 472 } else { 473 enabled_mode = "disabledByUser"; 474 } 475 group_data->SetString("enabledMode", enabled_mode); 476 477 bool always_allowed = false; 478 if (group_enabled) { 479 const base::DictionaryValue* whitelist = 480 profile->GetPrefs()->GetDictionary( 481 prefs::kContentSettingsPluginWhitelist); 482 whitelist->GetBoolean(group_identifier, &always_allowed); 483 } 484 group_data->SetBoolean("alwaysAllowed", always_allowed); 485 486 plugin_groups_data->Append(group_data); 487 } 488 base::DictionaryValue results; 489 results.Set("plugins", plugin_groups_data); 490 web_ui()->CallJavascriptFunction("returnPluginsData", results); 491 } 492 493 } // namespace 494 495 /////////////////////////////////////////////////////////////////////////////// 496 // 497 // PluginsUI 498 // 499 /////////////////////////////////////////////////////////////////////////////// 500 501 PluginsUI::PluginsUI(content::WebUI* web_ui) : WebUIController(web_ui) { 502 web_ui->AddMessageHandler(new PluginsDOMHandler()); 503 504 // Set up the chrome://plugins/ source. 505 Profile* profile = Profile::FromWebUI(web_ui); 506 content::WebUIDataSource::Add(profile, CreatePluginsUIHTMLSource(profile)); 507 } 508 509 // static 510 base::RefCountedMemory* PluginsUI::GetFaviconResourceBytes( 511 ui::ScaleFactor scale_factor) { 512 return ResourceBundle::GetSharedInstance(). 513 LoadDataResourceBytesForScale(IDR_PLUGINS_FAVICON, scale_factor); 514 } 515 516 // static 517 void PluginsUI::RegisterProfilePrefs( 518 user_prefs::PrefRegistrySyncable* registry) { 519 registry->RegisterBooleanPref( 520 prefs::kPluginsShowDetails, 521 false, 522 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); 523 registry->RegisterDictionaryPref( 524 prefs::kContentSettingsPluginWhitelist, 525 user_prefs::PrefRegistrySyncable::SYNCABLE_PREF); 526 } 527