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/prefs/scoped_user_pref_update.h" 22 #include "base/strings/utf_string_conversions.h" 23 #include "base/values.h" 24 #include "chrome/browser/chrome_notification_types.h" 25 #include "chrome/browser/content_settings/host_content_settings_map.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 "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 base::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 base::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 // Holds grouped plug-ins. The key is the group identifier and 175 // the value is the list of plug-ins belonging to the group. 176 typedef base::hash_map<std::string, std::vector<const WebPluginInfo*> > 177 PluginGroups; 178 179 // This pref guards the value whether about:plugins is in the details mode or 180 // not. 181 BooleanPrefMember show_details_; 182 183 base::WeakPtrFactory<PluginsDOMHandler> weak_ptr_factory_; 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 base::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 base::string16 adobereader = ASCIIToUTF16( 253 PluginMetadata::kAdobeReaderGroupName); 254 base::string16 internalpdf = 255 ASCIIToUTF16(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 base::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