| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2011 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/plugin_prefs.h" | |
| 6 | |
| 7 #include <string> | |
| 8 | |
| 9 #include "base/command_line.h" | |
| 10 #include "base/memory/scoped_ptr.h" | |
| 11 #include "base/memory/singleton.h" | |
| 12 #include "base/message_loop.h" | |
| 13 #include "base/path_service.h" | |
| 14 #include "base/utf_string_conversions.h" | |
| 15 #include "base/values.h" | |
| 16 #include "base/version.h" | |
| 17 #include "chrome/browser/prefs/pref_service.h" | |
| 18 #include "chrome/browser/prefs/scoped_user_pref_update.h" | |
| 19 #include "chrome/browser/profiles/profile.h" | |
| 20 #include "chrome/browser/profiles/profile_dependency_manager.h" | |
| 21 #include "chrome/browser/profiles/profile_keyed_service.h" | |
| 22 #include "chrome/browser/profiles/profile_keyed_service_factory.h" | |
| 23 #include "chrome/common/chrome_content_client.h" | |
| 24 #include "chrome/common/chrome_notification_types.h" | |
| 25 #include "chrome/common/chrome_paths.h" | |
| 26 #include "chrome/common/chrome_switches.h" | |
| 27 #include "chrome/common/pref_names.h" | |
| 28 #include "content/browser/browser_thread.h" | |
| 29 #include "content/common/notification_service.h" | |
| 30 #include "webkit/plugins/npapi/plugin_list.h" | |
| 31 #include "webkit/plugins/npapi/webplugininfo.h" | |
| 32 | |
| 33 namespace { | |
| 34 | |
| 35 class PluginPrefsWrapper : public ProfileKeyedService { | |
| 36 public: | |
| 37 explicit PluginPrefsWrapper(scoped_refptr<PluginPrefs> plugin_prefs) | |
| 38 : plugin_prefs_(plugin_prefs) {} | |
| 39 virtual ~PluginPrefsWrapper() {} | |
| 40 | |
| 41 PluginPrefs* plugin_prefs() { return plugin_prefs_.get(); } | |
| 42 | |
| 43 private: | |
| 44 // ProfileKeyedService methods: | |
| 45 virtual void Shutdown() OVERRIDE { | |
| 46 plugin_prefs_->ShutdownOnUIThread(); | |
| 47 } | |
| 48 | |
| 49 scoped_refptr<PluginPrefs> plugin_prefs_; | |
| 50 }; | |
| 51 | |
| 52 } | |
| 53 | |
| 54 // How long to wait to save the plugin enabled information, which might need to | |
| 55 // go to disk. | |
| 56 #define kPluginUpdateDelayMs (60 * 1000) | |
| 57 | |
| 58 class PluginPrefs::Factory : public ProfileKeyedServiceFactory { | |
| 59 public: | |
| 60 static Factory* GetInstance(); | |
| 61 | |
| 62 PluginPrefsWrapper* GetWrapperForProfile(Profile* profile); | |
| 63 | |
| 64 private: | |
| 65 friend struct DefaultSingletonTraits<Factory>; | |
| 66 | |
| 67 Factory(); | |
| 68 virtual ~Factory() {} | |
| 69 | |
| 70 // ProfileKeyedServiceFactory methods: | |
| 71 virtual ProfileKeyedService* BuildServiceInstanceFor( | |
| 72 Profile* profile) const OVERRIDE; | |
| 73 virtual bool ServiceRedirectedInIncognito() OVERRIDE { return true; } | |
| 74 virtual bool ServiceIsNULLWhileTesting() OVERRIDE { return true; } | |
| 75 }; | |
| 76 | |
| 77 // static | |
| 78 void PluginPrefs::Initialize() { | |
| 79 Factory::GetInstance(); | |
| 80 } | |
| 81 | |
| 82 // static | |
| 83 PluginPrefs* PluginPrefs::GetForProfile(Profile* profile) { | |
| 84 PluginPrefs* plugin_prefs = | |
| 85 Factory::GetInstance()->GetWrapperForProfile(profile)->plugin_prefs(); | |
| 86 DCHECK(plugin_prefs); | |
| 87 return plugin_prefs; | |
| 88 } | |
| 89 | |
| 90 DictionaryValue* PluginPrefs::CreatePluginFileSummary( | |
| 91 const webkit::npapi::WebPluginInfo& plugin) { | |
| 92 DictionaryValue* data = new DictionaryValue(); | |
| 93 data->SetString("path", plugin.path.value()); | |
| 94 data->SetString("name", plugin.name); | |
| 95 data->SetString("version", plugin.version); | |
| 96 data->SetBoolean("enabled", IsPluginEnabled(plugin)); | |
| 97 return data; | |
| 98 } | |
| 99 | |
| 100 void PluginPrefs::EnablePluginGroup(bool enable, const string16& group_name) { | |
| 101 webkit::npapi::PluginList::Singleton()->EnableGroup(enable, group_name); | |
| 102 NotifyPluginStatusChanged(); | |
| 103 } | |
| 104 | |
| 105 void PluginPrefs::EnablePlugin(bool enable, const FilePath& path) { | |
| 106 if (enable) | |
| 107 webkit::npapi::PluginList::Singleton()->EnablePlugin(path); | |
| 108 else | |
| 109 webkit::npapi::PluginList::Singleton()->DisablePlugin(path); | |
| 110 | |
| 111 NotifyPluginStatusChanged(); | |
| 112 } | |
| 113 | |
| 114 bool PluginPrefs::IsPluginEnabled(const webkit::npapi::WebPluginInfo& plugin) { | |
| 115 // If enabling NaCl, make sure the plugin is also enabled. See bug | |
| 116 // http://code.google.com/p/chromium/issues/detail?id=81010 for more | |
| 117 // information. | |
| 118 // TODO(dspringer): When NaCl is on by default, remove this code. | |
| 119 if ((plugin.name == | |
| 120 ASCIIToUTF16(chrome::ChromeContentClient::kNaClPluginName)) && | |
| 121 CommandLine::ForCurrentProcess()->HasSwitch(switches::kEnableNaCl)) { | |
| 122 return true; | |
| 123 } | |
| 124 return webkit::npapi::IsPluginEnabled(plugin); | |
| 125 } | |
| 126 | |
| 127 void PluginPrefs::Observe(int type, | |
| 128 const NotificationSource& source, | |
| 129 const NotificationDetails& details) { | |
| 130 DCHECK_EQ(chrome::NOTIFICATION_PREF_CHANGED, type); | |
| 131 const std::string* pref_name = Details<std::string>(details).ptr(); | |
| 132 if (!pref_name) { | |
| 133 NOTREACHED(); | |
| 134 return; | |
| 135 } | |
| 136 DCHECK_EQ(prefs_, Source<PrefService>(source).ptr()); | |
| 137 if (*pref_name == prefs::kPluginsDisabledPlugins || | |
| 138 *pref_name == prefs::kPluginsDisabledPluginsExceptions || | |
| 139 *pref_name == prefs::kPluginsEnabledPlugins) { | |
| 140 const ListValue* disabled_list = | |
| 141 prefs_->GetList(prefs::kPluginsDisabledPlugins); | |
| 142 const ListValue* exceptions_list = | |
| 143 prefs_->GetList(prefs::kPluginsDisabledPluginsExceptions); | |
| 144 const ListValue* enabled_list = | |
| 145 prefs_->GetList(prefs::kPluginsEnabledPlugins); | |
| 146 UpdatePluginsStateFromPolicy(disabled_list, exceptions_list, enabled_list); | |
| 147 } | |
| 148 } | |
| 149 | |
| 150 void PluginPrefs::UpdatePluginsStateFromPolicy( | |
| 151 const ListValue* disabled_list, | |
| 152 const ListValue* exceptions_list, | |
| 153 const ListValue* enabled_list) { | |
| 154 std::set<string16> disabled_plugin_patterns; | |
| 155 std::set<string16> disabled_plugin_exception_patterns; | |
| 156 std::set<string16> enabled_plugin_patterns; | |
| 157 | |
| 158 ListValueToStringSet(disabled_list, &disabled_plugin_patterns); | |
| 159 ListValueToStringSet(exceptions_list, &disabled_plugin_exception_patterns); | |
| 160 ListValueToStringSet(enabled_list, &enabled_plugin_patterns); | |
| 161 | |
| 162 webkit::npapi::PluginGroup::SetPolicyEnforcedPluginPatterns( | |
| 163 disabled_plugin_patterns, | |
| 164 disabled_plugin_exception_patterns, | |
| 165 enabled_plugin_patterns); | |
| 166 | |
| 167 NotifyPluginStatusChanged(); | |
| 168 } | |
| 169 | |
| 170 void PluginPrefs::ListValueToStringSet(const ListValue* src, | |
| 171 std::set<string16>* dest) { | |
| 172 DCHECK(src); | |
| 173 DCHECK(dest); | |
| 174 ListValue::const_iterator end(src->end()); | |
| 175 for (ListValue::const_iterator current(src->begin()); | |
| 176 current != end; ++current) { | |
| 177 string16 plugin_name; | |
| 178 if ((*current)->GetAsString(&plugin_name)) { | |
| 179 dest->insert(plugin_name); | |
| 180 } | |
| 181 } | |
| 182 } | |
| 183 | |
| 184 void PluginPrefs::SetProfile(Profile* profile) { | |
| 185 prefs_ = profile->GetPrefs(); | |
| 186 bool update_internal_dir = false; | |
| 187 FilePath last_internal_dir = | |
| 188 prefs_->GetFilePath(prefs::kPluginsLastInternalDirectory); | |
| 189 FilePath cur_internal_dir; | |
| 190 if (PathService::Get(chrome::DIR_INTERNAL_PLUGINS, &cur_internal_dir) && | |
| 191 cur_internal_dir != last_internal_dir) { | |
| 192 update_internal_dir = true; | |
| 193 prefs_->SetFilePath( | |
| 194 prefs::kPluginsLastInternalDirectory, cur_internal_dir); | |
| 195 } | |
| 196 | |
| 197 bool force_enable_internal_pdf = false; | |
| 198 bool internal_pdf_enabled = false; | |
| 199 string16 pdf_group_name = | |
| 200 ASCIIToUTF16(chrome::ChromeContentClient::kPDFPluginName); | |
| 201 FilePath pdf_path; | |
| 202 PathService::Get(chrome::FILE_PDF_PLUGIN, &pdf_path); | |
| 203 FilePath::StringType pdf_path_str = pdf_path.value(); | |
| 204 if (!prefs_->GetBoolean(prefs::kPluginsEnabledInternalPDF)) { | |
| 205 // We switched to the internal pdf plugin being on by default, and so we | |
| 206 // need to force it to be enabled. We only want to do it this once though, | |
| 207 // i.e. we don't want to enable it again if the user disables it afterwards. | |
| 208 prefs_->SetBoolean(prefs::kPluginsEnabledInternalPDF, true); | |
| 209 force_enable_internal_pdf = true; | |
| 210 } | |
| 211 | |
| 212 { // Scoped update of prefs::kPluginsPluginsList. | |
| 213 ListPrefUpdate update(prefs_, prefs::kPluginsPluginsList); | |
| 214 ListValue* saved_plugins_list = update.Get(); | |
| 215 if (saved_plugins_list && !saved_plugins_list->empty()) { | |
| 216 for (ListValue::const_iterator it = saved_plugins_list->begin(); | |
| 217 it != saved_plugins_list->end(); | |
| 218 ++it) { | |
| 219 if (!(*it)->IsType(Value::TYPE_DICTIONARY)) { | |
| 220 LOG(WARNING) << "Invalid entry in " << prefs::kPluginsPluginsList; | |
| 221 continue; // Oops, don't know what to do with this item. | |
| 222 } | |
| 223 | |
| 224 DictionaryValue* plugin = static_cast<DictionaryValue*>(*it); | |
| 225 string16 group_name; | |
| 226 bool enabled; | |
| 227 if (!plugin->GetBoolean("enabled", &enabled)) | |
| 228 enabled = true; | |
| 229 | |
| 230 FilePath::StringType path; | |
| 231 // The plugin list constains all the plugin files in addition to the | |
| 232 // plugin groups. | |
| 233 if (plugin->GetString("path", &path)) { | |
| 234 // Files have a path attribute, groups don't. | |
| 235 FilePath plugin_path(path); | |
| 236 if (update_internal_dir && | |
| 237 FilePath::CompareIgnoreCase(plugin_path.DirName().value(), | |
| 238 last_internal_dir.value()) == 0) { | |
| 239 // If the internal plugin directory has changed and if the plugin | |
| 240 // looks internal, update its path in the prefs. | |
| 241 plugin_path = cur_internal_dir.Append(plugin_path.BaseName()); | |
| 242 path = plugin_path.value(); | |
| 243 plugin->SetString("path", path); | |
| 244 } | |
| 245 | |
| 246 if (FilePath::CompareIgnoreCase(path, pdf_path_str) == 0) { | |
| 247 if (!enabled && force_enable_internal_pdf) { | |
| 248 enabled = true; | |
| 249 plugin->SetBoolean("enabled", true); | |
| 250 } | |
| 251 | |
| 252 internal_pdf_enabled = enabled; | |
| 253 } | |
| 254 | |
| 255 if (!enabled) | |
| 256 webkit::npapi::PluginList::Singleton()->DisablePlugin(plugin_path); | |
| 257 } else if (!enabled && plugin->GetString("name", &group_name)) { | |
| 258 // Don't disable this group if it's for the pdf plugin and we just | |
| 259 // forced it on. | |
| 260 if (force_enable_internal_pdf && pdf_group_name == group_name) | |
| 261 continue; | |
| 262 | |
| 263 // Otherwise this is a list of groups. | |
| 264 EnablePluginGroup(false, group_name); | |
| 265 } | |
| 266 } | |
| 267 } else { | |
| 268 // If the saved plugin list is empty, then the call to UpdatePreferences() | |
| 269 // below failed in an earlier run, possibly because the user closed the | |
| 270 // browser too quickly. Try to force enable the internal PDF plugin again. | |
| 271 force_enable_internal_pdf = true; | |
| 272 } | |
| 273 } // Scoped update of prefs::kPluginsPluginsList. | |
| 274 | |
| 275 // Build the set of policy enabled/disabled plugin patterns once and cache it. | |
| 276 // Don't do this in the constructor, there's no profile available there. | |
| 277 const ListValue* disabled_plugins = | |
| 278 prefs_->GetList(prefs::kPluginsDisabledPlugins); | |
| 279 const ListValue* disabled_exception_plugins = | |
| 280 prefs_->GetList(prefs::kPluginsDisabledPluginsExceptions); | |
| 281 const ListValue* enabled_plugins = | |
| 282 prefs_->GetList(prefs::kPluginsEnabledPlugins); | |
| 283 UpdatePluginsStateFromPolicy(disabled_plugins, | |
| 284 disabled_exception_plugins, | |
| 285 enabled_plugins); | |
| 286 | |
| 287 registrar_.RemoveAll(); | |
| 288 registrar_.Init(prefs_); | |
| 289 registrar_.Add(prefs::kPluginsDisabledPlugins, this); | |
| 290 registrar_.Add(prefs::kPluginsDisabledPluginsExceptions, this); | |
| 291 registrar_.Add(prefs::kPluginsEnabledPlugins, this); | |
| 292 | |
| 293 if (force_enable_internal_pdf || internal_pdf_enabled) { | |
| 294 // See http://crbug.com/50105 for background. | |
| 295 EnablePluginGroup(false, ASCIIToUTF16( | |
| 296 webkit::npapi::PluginGroup::kAdobeReaderGroupName)); | |
| 297 } | |
| 298 | |
| 299 if (force_enable_internal_pdf) { | |
| 300 // We want to save this, but doing so requires loading the list of plugins, | |
| 301 // so do it after a minute as to not impact startup performance. Note that | |
| 302 // plugins are loaded after 30s by the metrics service. | |
| 303 UpdatePreferences(kPluginUpdateDelayMs); | |
| 304 } | |
| 305 } | |
| 306 | |
| 307 void PluginPrefs::ShutdownOnUIThread() { | |
| 308 prefs_ = NULL; | |
| 309 registrar_.RemoveAll(); | |
| 310 } | |
| 311 | |
| 312 // static | |
| 313 PluginPrefs::Factory* PluginPrefs::Factory::GetInstance() { | |
| 314 return Singleton<PluginPrefs::Factory>::get(); | |
| 315 } | |
| 316 | |
| 317 PluginPrefsWrapper* PluginPrefs::Factory::GetWrapperForProfile( | |
| 318 Profile* profile) { | |
| 319 return static_cast<PluginPrefsWrapper*>(GetServiceForProfile(profile, true)); | |
| 320 } | |
| 321 | |
| 322 PluginPrefs::Factory::Factory() | |
| 323 : ProfileKeyedServiceFactory(ProfileDependencyManager::GetInstance()) { | |
| 324 } | |
| 325 | |
| 326 ProfileKeyedService* PluginPrefs::Factory::BuildServiceInstanceFor( | |
| 327 Profile* profile) const { | |
| 328 scoped_refptr<PluginPrefs> plugin_prefs(new PluginPrefs()); | |
| 329 plugin_prefs->SetProfile(profile); | |
| 330 return new PluginPrefsWrapper(plugin_prefs); | |
| 331 } | |
| 332 | |
| 333 PluginPrefs::PluginPrefs() : notify_pending_(false) { | |
| 334 } | |
| 335 | |
| 336 PluginPrefs::~PluginPrefs() { | |
| 337 } | |
| 338 | |
| 339 void PluginPrefs::UpdatePreferences(int delay_ms) { | |
| 340 BrowserThread::PostDelayedTask( | |
| 341 BrowserThread::FILE, | |
| 342 FROM_HERE, | |
| 343 NewRunnableMethod(this, &PluginPrefs::GetPreferencesDataOnFileThread), | |
| 344 delay_ms); | |
| 345 } | |
| 346 | |
| 347 void PluginPrefs::GetPreferencesDataOnFileThread() { | |
| 348 std::vector<webkit::npapi::WebPluginInfo> plugins; | |
| 349 std::vector<webkit::npapi::PluginGroup> groups; | |
| 350 | |
| 351 webkit::npapi::PluginList* plugin_list = | |
| 352 webkit::npapi::PluginList::Singleton(); | |
| 353 plugin_list->GetPlugins(&plugins); | |
| 354 plugin_list->GetPluginGroups(false, &groups); | |
| 355 | |
| 356 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | |
| 357 NewRunnableMethod(this, &PluginPrefs::OnUpdatePreferences, | |
| 358 plugins, groups)); | |
| 359 } | |
| 360 | |
| 361 void PluginPrefs::OnUpdatePreferences( | |
| 362 std::vector<webkit::npapi::WebPluginInfo> plugins, | |
| 363 std::vector<webkit::npapi::PluginGroup> groups) { | |
| 364 if (!prefs_) | |
| 365 return; | |
| 366 | |
| 367 ListPrefUpdate update(prefs_, prefs::kPluginsPluginsList); | |
| 368 ListValue* plugins_list = update.Get(); | |
| 369 plugins_list->Clear(); | |
| 370 | |
| 371 FilePath internal_dir; | |
| 372 if (PathService::Get(chrome::DIR_INTERNAL_PLUGINS, &internal_dir)) | |
| 373 prefs_->SetFilePath(prefs::kPluginsLastInternalDirectory, internal_dir); | |
| 374 | |
| 375 // Add the plugin files. | |
| 376 for (size_t i = 0; i < plugins.size(); ++i) { | |
| 377 DictionaryValue* summary = CreatePluginFileSummary(plugins[i]); | |
| 378 // If the plugin is managed by policy, store the user preferred state | |
| 379 // instead. | |
| 380 if (plugins[i].enabled & | |
| 381 webkit::npapi::WebPluginInfo::MANAGED_MASK) { | |
| 382 bool user_enabled = | |
| 383 (plugins[i].enabled & webkit::npapi::WebPluginInfo::USER_MASK) == | |
| 384 webkit::npapi::WebPluginInfo::USER_ENABLED; | |
| 385 summary->SetBoolean("enabled", user_enabled); | |
| 386 } | |
| 387 plugins_list->Append(summary); | |
| 388 } | |
| 389 | |
| 390 // Add the groups as well. | |
| 391 for (size_t i = 0; i < groups.size(); ++i) { | |
| 392 DictionaryValue* summary = groups[i].GetSummary(); | |
| 393 // If the plugin is disabled only by policy don't store this state in the | |
| 394 // user pref store. | |
| 395 if (!groups[i].Enabled() && | |
| 396 webkit::npapi::PluginGroup::IsPluginNameDisabledByPolicy( | |
| 397 groups[i].GetGroupName())) | |
| 398 summary->SetBoolean("enabled", true); | |
| 399 plugins_list->Append(summary); | |
| 400 } | |
| 401 } | |
| 402 | |
| 403 void PluginPrefs::NotifyPluginStatusChanged() { | |
| 404 if (notify_pending_) | |
| 405 return; | |
| 406 notify_pending_ = true; | |
| 407 MessageLoop::current()->PostTask( | |
| 408 FROM_HERE, | |
| 409 NewRunnableMethod(this, &PluginPrefs::OnNotifyPluginStatusChanged)); | |
| 410 } | |
| 411 | |
| 412 void PluginPrefs::OnNotifyPluginStatusChanged() { | |
| 413 notify_pending_ = false; | |
| 414 NotificationService::current()->Notify( | |
| 415 content::NOTIFICATION_PLUGIN_ENABLE_STATUS_CHANGED, | |
| 416 Source<PluginPrefs>(this), | |
| 417 NotificationService::NoDetails()); | |
| 418 } | |
| 419 | |
| 420 /*static*/ | |
| 421 void PluginPrefs::RegisterPrefs(PrefService* prefs) { | |
| 422 FilePath internal_dir; | |
| 423 PathService::Get(chrome::DIR_INTERNAL_PLUGINS, &internal_dir); | |
| 424 prefs->RegisterFilePathPref(prefs::kPluginsLastInternalDirectory, | |
| 425 internal_dir, | |
| 426 PrefService::UNSYNCABLE_PREF); | |
| 427 prefs->RegisterListPref(prefs::kPluginsDisabledPlugins, | |
| 428 PrefService::UNSYNCABLE_PREF); | |
| 429 prefs->RegisterListPref(prefs::kPluginsDisabledPluginsExceptions, | |
| 430 PrefService::UNSYNCABLE_PREF); | |
| 431 prefs->RegisterListPref(prefs::kPluginsEnabledPlugins, | |
| 432 PrefService::UNSYNCABLE_PREF); | |
| 433 } | |
| OLD | NEW |