[Media Router] Add integration tests and e2e tests for media router and presentation...
[chromium-blink-merge.git] / chrome / browser / plugins / plugin_prefs.cc
blob8eaa104d28c1adb38f75a8ef4abfb992d855f79b
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.
5 #include "chrome/browser/plugins/plugin_prefs.h"
7 #include <string>
9 #include "base/bind.h"
10 #include "base/command_line.h"
11 #include "base/location.h"
12 #include "base/memory/scoped_ptr.h"
13 #include "base/path_service.h"
14 #include "base/prefs/scoped_user_pref_update.h"
15 #include "base/single_thread_task_runner.h"
16 #include "base/strings/string_util.h"
17 #include "base/strings/utf_string_conversions.h"
18 #include "base/thread_task_runner_handle.h"
19 #include "base/values.h"
20 #include "build/build_config.h"
21 #include "chrome/browser/browser_process.h"
22 #include "chrome/browser/chrome_notification_types.h"
23 #include "chrome/browser/plugins/plugin_installer.h"
24 #include "chrome/browser/plugins/plugin_metadata.h"
25 #include "chrome/browser/plugins/plugin_prefs_factory.h"
26 #include "chrome/browser/profiles/profile.h"
27 #include "chrome/common/chrome_constants.h"
28 #include "chrome/common/chrome_paths.h"
29 #include "chrome/common/chrome_switches.h"
30 #include "chrome/common/pref_names.h"
31 #include "components/keyed_service/core/keyed_service.h"
32 #include "content/public/browser/browser_thread.h"
33 #include "content/public/browser/notification_service.h"
34 #include "content/public/browser/plugin_service.h"
35 #include "content/public/common/webplugininfo.h"
37 #if !defined(DISABLE_NACL)
38 #include "components/nacl/common/nacl_constants.h"
39 #endif
41 using content::BrowserThread;
42 using content::PluginService;
44 namespace {
46 bool IsComponentUpdatedPepperFlash(const base::FilePath& plugin) {
47 if (plugin.BaseName().value() == chrome::kPepperFlashPluginFilename) {
48 base::FilePath component_updated_pepper_flash_dir;
49 if (PathService::Get(chrome::DIR_COMPONENT_UPDATED_PEPPER_FLASH_PLUGIN,
50 &component_updated_pepper_flash_dir) &&
51 component_updated_pepper_flash_dir.IsParent(plugin)) {
52 return true;
56 return false;
59 } // namespace
61 PluginPrefs::PluginState::PluginState() {
64 PluginPrefs::PluginState::~PluginState() {
67 bool PluginPrefs::PluginState::Get(const base::FilePath& plugin,
68 bool* enabled) const {
69 base::FilePath key = ConvertMapKey(plugin);
70 std::map<base::FilePath, bool>::const_iterator iter = state_.find(key);
71 if (iter != state_.end()) {
72 *enabled = iter->second;
73 return true;
75 return false;
78 void PluginPrefs::PluginState::Set(const base::FilePath& plugin, bool enabled) {
79 state_[ConvertMapKey(plugin)] = enabled;
82 base::FilePath PluginPrefs::PluginState::ConvertMapKey(
83 const base::FilePath& plugin) const {
84 // Keep the state of component-updated and bundled Pepper Flash in sync.
85 if (IsComponentUpdatedPepperFlash(plugin)) {
86 base::FilePath bundled_pepper_flash;
87 if (PathService::Get(chrome::FILE_PEPPER_FLASH_PLUGIN,
88 &bundled_pepper_flash)) {
89 return bundled_pepper_flash;
93 return plugin;
96 // static
97 scoped_refptr<PluginPrefs> PluginPrefs::GetForProfile(Profile* profile) {
98 return PluginPrefsFactory::GetPrefsForProfile(profile);
101 // static
102 scoped_refptr<PluginPrefs> PluginPrefs::GetForTestingProfile(
103 Profile* profile) {
104 return static_cast<PluginPrefs*>(
105 PluginPrefsFactory::GetInstance()->SetTestingFactoryAndUse(
106 profile, &PluginPrefsFactory::CreateForTestingProfile).get());
109 void PluginPrefs::EnablePluginGroup(bool enabled,
110 const base::string16& group_name) {
111 PluginService::GetInstance()->GetPlugins(
112 base::Bind(&PluginPrefs::EnablePluginGroupInternal,
113 this, enabled, group_name));
116 void PluginPrefs::EnablePluginGroupInternal(
117 bool enabled,
118 const base::string16& group_name,
119 const std::vector<content::WebPluginInfo>& plugins) {
120 base::AutoLock auto_lock(lock_);
121 PluginFinder* finder = PluginFinder::GetInstance();
123 // Set the desired state for the group.
124 plugin_group_state_[group_name] = enabled;
126 // Update the state for all plugins in the group.
127 for (size_t i = 0; i < plugins.size(); ++i) {
128 scoped_ptr<PluginMetadata> plugin(finder->GetPluginMetadata(plugins[i]));
129 if (group_name != plugin->name())
130 continue;
131 plugin_state_.Set(plugins[i].path, enabled);
134 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
135 base::Bind(&PluginPrefs::OnUpdatePreferences, this, plugins));
136 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
137 base::Bind(&PluginPrefs::NotifyPluginStatusChanged, this));
140 void PluginPrefs::EnablePlugin(
141 bool enabled, const base::FilePath& path,
142 const base::Callback<void(bool)>& callback) {
143 PluginFinder* finder = PluginFinder::GetInstance();
144 content::WebPluginInfo plugin;
145 bool can_enable = true;
146 if (PluginService::GetInstance()->GetPluginInfoByPath(path, &plugin)) {
147 scoped_ptr<PluginMetadata> plugin_metadata(
148 finder->GetPluginMetadata(plugin));
149 PolicyStatus plugin_status = PolicyStatusForPlugin(plugin.name);
150 PolicyStatus group_status = PolicyStatusForPlugin(plugin_metadata->name());
151 if (enabled) {
152 if (plugin_status == POLICY_DISABLED || group_status == POLICY_DISABLED)
153 can_enable = false;
154 } else {
155 if (plugin_status == POLICY_ENABLED || group_status == POLICY_ENABLED)
156 can_enable = false;
158 } else {
159 NOTREACHED();
162 if (!can_enable) {
163 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
164 base::Bind(callback, false));
165 return;
168 PluginService::GetInstance()->GetPlugins(
169 base::Bind(&PluginPrefs::EnablePluginInternal, this,
170 enabled, path, finder, callback));
173 void PluginPrefs::EnablePluginInternal(
174 bool enabled,
175 const base::FilePath& path,
176 PluginFinder* plugin_finder,
177 const base::Callback<void(bool)>& callback,
178 const std::vector<content::WebPluginInfo>& plugins) {
180 // Set the desired state for the plugin.
181 base::AutoLock auto_lock(lock_);
182 plugin_state_.Set(path, enabled);
185 base::string16 group_name;
186 for (size_t i = 0; i < plugins.size(); ++i) {
187 if (plugins[i].path == path) {
188 scoped_ptr<PluginMetadata> plugin_metadata(
189 plugin_finder->GetPluginMetadata(plugins[i]));
190 // set the group name for this plugin.
191 group_name = plugin_metadata->name();
192 DCHECK_EQ(enabled, IsPluginEnabled(plugins[i]));
193 break;
197 bool all_disabled = true;
198 for (size_t i = 0; i < plugins.size(); ++i) {
199 scoped_ptr<PluginMetadata> plugin_metadata(
200 plugin_finder->GetPluginMetadata(plugins[i]));
201 DCHECK(!plugin_metadata->name().empty());
202 if (group_name == plugin_metadata->name()) {
203 all_disabled = all_disabled && !IsPluginEnabled(plugins[i]);
207 if (!group_name.empty()) {
208 // Update the state for the corresponding plugin group.
209 base::AutoLock auto_lock(lock_);
210 plugin_group_state_[group_name] = !all_disabled;
213 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
214 base::Bind(&PluginPrefs::OnUpdatePreferences, this, plugins));
215 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
216 base::Bind(&PluginPrefs::NotifyPluginStatusChanged, this));
217 callback.Run(true);
220 PluginPrefs::PolicyStatus PluginPrefs::PolicyStatusForPlugin(
221 const base::string16& name) const {
222 base::AutoLock auto_lock(lock_);
223 if (IsStringMatchedInSet(name, policy_enabled_plugin_patterns_)) {
224 return POLICY_ENABLED;
225 } else if (IsStringMatchedInSet(name, policy_disabled_plugin_patterns_) &&
226 !IsStringMatchedInSet(
227 name, policy_disabled_plugin_exception_patterns_)) {
228 return POLICY_DISABLED;
229 } else {
230 return NO_POLICY;
234 bool PluginPrefs::IsPluginEnabled(const content::WebPluginInfo& plugin) const {
235 scoped_ptr<PluginMetadata> plugin_metadata(
236 PluginFinder::GetInstance()->GetPluginMetadata(plugin));
237 base::string16 group_name = plugin_metadata->name();
239 // Check if the plugin or its group is enabled by policy.
240 PolicyStatus plugin_status = PolicyStatusForPlugin(plugin.name);
241 PolicyStatus group_status = PolicyStatusForPlugin(group_name);
242 if (plugin_status == POLICY_ENABLED || group_status == POLICY_ENABLED)
243 return true;
245 // Check if the plugin or its group is disabled by policy.
246 if (plugin_status == POLICY_DISABLED || group_status == POLICY_DISABLED)
247 return false;
249 #if !defined(DISABLE_NACL)
250 // If enabling NaCl, make sure the plugin is also enabled. See bug
251 // http://code.google.com/p/chromium/issues/detail?id=81010 for more
252 // information.
253 // TODO(dspringer): When NaCl is on by default, remove this code.
254 if ((plugin.name == base::ASCIIToUTF16(nacl::kNaClPluginName)) &&
255 base::CommandLine::ForCurrentProcess()->HasSwitch(
256 switches::kEnableNaCl)) {
257 return true;
259 #endif
261 base::AutoLock auto_lock(lock_);
262 // Check user preferences for the plugin.
263 bool plugin_enabled = false;
264 if (plugin_state_.Get(plugin.path, &plugin_enabled))
265 return plugin_enabled;
267 // Check user preferences for the plugin group.
268 std::map<base::string16, bool>::const_iterator group_it(
269 plugin_group_state_.find(group_name));
270 if (group_it != plugin_group_state_.end())
271 return group_it->second;
273 // Default to enabled.
274 return true;
277 void PluginPrefs::UpdatePatternsAndNotify(std::set<base::string16>* patterns,
278 const std::string& pref_name) {
279 base::AutoLock auto_lock(lock_);
280 ListValueToStringSet(prefs_->GetList(pref_name.c_str()), patterns);
282 NotifyPluginStatusChanged();
285 /*static*/
286 bool PluginPrefs::IsStringMatchedInSet(
287 const base::string16& name,
288 const std::set<base::string16>& pattern_set) {
289 std::set<base::string16>::const_iterator pattern(pattern_set.begin());
290 while (pattern != pattern_set.end()) {
291 if (MatchPattern(name, *pattern))
292 return true;
293 ++pattern;
296 return false;
299 /* static */
300 void PluginPrefs::ListValueToStringSet(const base::ListValue* src,
301 std::set<base::string16>* dest) {
302 DCHECK(src);
303 DCHECK(dest);
304 dest->clear();
305 base::ListValue::const_iterator end(src->end());
306 for (base::ListValue::const_iterator current(src->begin());
307 current != end; ++current) {
308 base::string16 plugin_name;
309 if ((*current)->GetAsString(&plugin_name)) {
310 dest->insert(plugin_name);
315 void PluginPrefs::SetPrefs(PrefService* prefs) {
316 prefs_ = prefs;
317 bool update_internal_dir = false;
318 base::FilePath last_internal_dir =
319 prefs_->GetFilePath(prefs::kPluginsLastInternalDirectory);
320 base::FilePath cur_internal_dir;
321 if (PathService::Get(chrome::DIR_INTERNAL_PLUGINS, &cur_internal_dir) &&
322 cur_internal_dir != last_internal_dir) {
323 update_internal_dir = true;
324 prefs_->SetFilePath(
325 prefs::kPluginsLastInternalDirectory, cur_internal_dir);
328 bool migrate_to_pepper_flash = false;
329 #if defined(OS_WIN) || defined(OS_MACOSX)
330 // If bundled NPAPI Flash is enabled while Pepper Flash is disabled, we
331 // would like to turn Pepper Flash on. And we only want to do it once.
332 // TODO(yzshen): Remove all |migrate_to_pepper_flash|-related code after it
333 // has been run once by most users. (Maybe Chrome 24 or Chrome 25.)
334 // NOTE(shess): Keep in mind that Mac is on a different schedule.
335 if (!prefs_->GetBoolean(prefs::kPluginsMigratedToPepperFlash)) {
336 prefs_->SetBoolean(prefs::kPluginsMigratedToPepperFlash, true);
337 migrate_to_pepper_flash = true;
339 #endif
341 bool remove_component_pepper_flash_settings = false;
342 // If component-updated Pepper Flash is disabled, we would like to remove that
343 // settings item. And we only want to do it once. (Please see the comments of
344 // kPluginsRemovedOldComponentPepperFlashSettings for why.)
345 // TODO(yzshen): Remove all |remove_component_pepper_flash_settings|-related
346 // code after it has been run once by most users.
347 if (!prefs_->GetBoolean(
348 prefs::kPluginsRemovedOldComponentPepperFlashSettings)) {
349 prefs_->SetBoolean(prefs::kPluginsRemovedOldComponentPepperFlashSettings,
350 true);
351 remove_component_pepper_flash_settings = true;
354 { // Scoped update of prefs::kPluginsPluginsList.
355 ListPrefUpdate update(prefs_, prefs::kPluginsPluginsList);
356 base::ListValue* saved_plugins_list = update.Get();
357 if (saved_plugins_list && !saved_plugins_list->empty()) {
358 // The following four variables are only valid when
359 // |migrate_to_pepper_flash| is set to true.
360 base::FilePath npapi_flash;
361 base::FilePath pepper_flash;
362 base::DictionaryValue* pepper_flash_node = NULL;
363 bool npapi_flash_enabled = false;
364 if (migrate_to_pepper_flash) {
365 PathService::Get(chrome::FILE_FLASH_PLUGIN, &npapi_flash);
366 PathService::Get(chrome::FILE_PEPPER_FLASH_PLUGIN, &pepper_flash);
369 // Used when |remove_component_pepper_flash_settings| is set to true.
370 base::ListValue::iterator component_pepper_flash_node =
371 saved_plugins_list->end();
373 for (base::ListValue::iterator it = saved_plugins_list->begin();
374 it != saved_plugins_list->end();
375 ++it) {
376 if (!(*it)->IsType(base::Value::TYPE_DICTIONARY)) {
377 LOG(WARNING) << "Invalid entry in " << prefs::kPluginsPluginsList;
378 continue; // Oops, don't know what to do with this item.
381 base::DictionaryValue* plugin =
382 static_cast<base::DictionaryValue*>(*it);
383 base::string16 group_name;
384 bool enabled;
385 if (!plugin->GetBoolean("enabled", &enabled))
386 enabled = true;
388 base::FilePath::StringType path;
389 // The plugin list constains all the plugin files in addition to the
390 // plugin groups.
391 if (plugin->GetString("path", &path)) {
392 // Files have a path attribute, groups don't.
393 base::FilePath plugin_path(path);
395 // The path to the intenral plugin directory changes everytime Chrome
396 // is auto-updated, since it contains the current version number. For
397 // example, it changes from foobar\Chrome\Application\21.0.1180.83 to
398 // foobar\Chrome\Application\21.0.1180.89.
399 // However, we would like the settings of internal plugins to persist
400 // across Chrome updates. Therefore, we need to recognize those paths
401 // that are within the previous internal plugin directory, and update
402 // them in the prefs accordingly.
403 if (update_internal_dir) {
404 base::FilePath relative_path;
406 // Extract the part of |plugin_path| that is relative to
407 // |last_internal_dir|. For example, |relative_path| will be
408 // foo\bar.dll if |plugin_path| is <last_internal_dir>\foo\bar.dll.
410 // Every iteration the last path component from |plugin_path| is
411 // removed and prepended to |relative_path| until we get up to
412 // |last_internal_dir|.
413 while (last_internal_dir.IsParent(plugin_path)) {
414 relative_path = plugin_path.BaseName().Append(relative_path);
416 base::FilePath old_path = plugin_path;
417 plugin_path = plugin_path.DirName();
418 // To be extra sure that we won't end up in an infinite loop.
419 if (old_path == plugin_path) {
420 NOTREACHED();
421 break;
425 // If |relative_path| is empty, |plugin_path| is not within
426 // |last_internal_dir|. We don't need to update it.
427 if (!relative_path.empty()) {
428 plugin_path = cur_internal_dir.Append(relative_path);
429 path = plugin_path.value();
430 plugin->SetString("path", path);
434 if (migrate_to_pepper_flash &&
435 base::FilePath::CompareEqualIgnoreCase(
436 path, npapi_flash.value())) {
437 npapi_flash_enabled = enabled;
438 } else if (migrate_to_pepper_flash &&
439 base::FilePath::CompareEqualIgnoreCase(
440 path, pepper_flash.value())) {
441 if (!enabled)
442 pepper_flash_node = plugin;
443 } else if (remove_component_pepper_flash_settings &&
444 IsComponentUpdatedPepperFlash(plugin_path)) {
445 if (!enabled) {
446 component_pepper_flash_node = it;
447 // Skip setting |enabled| into |plugin_state_|.
448 continue;
452 plugin_state_.Set(plugin_path, enabled);
453 } else if (!enabled && plugin->GetString("name", &group_name)) {
454 // Otherwise this is a list of groups.
455 plugin_group_state_[group_name] = false;
459 if (npapi_flash_enabled && pepper_flash_node) {
460 DCHECK(migrate_to_pepper_flash);
461 pepper_flash_node->SetBoolean("enabled", true);
462 plugin_state_.Set(pepper_flash, true);
465 if (component_pepper_flash_node != saved_plugins_list->end()) {
466 DCHECK(remove_component_pepper_flash_settings);
467 saved_plugins_list->Erase(component_pepper_flash_node, NULL);
469 } else {
470 // If the saved plugin list is empty, then the call to UpdatePreferences()
471 // below failed in an earlier run, possibly because the user closed the
472 // browser too quickly.
474 // Only want one PDF plugin enabled at a time. See http://crbug.com/50105
475 // for background.
476 plugin_group_state_[base::ASCIIToUTF16(
477 PluginMetadata::kAdobeReaderGroupName)] = false;
479 } // Scoped update of prefs::kPluginsPluginsList.
481 // Build the set of policy enabled/disabled plugin patterns once and cache it.
482 // Don't do this in the constructor, there's no profile available there.
483 ListValueToStringSet(prefs_->GetList(prefs::kPluginsDisabledPlugins),
484 &policy_disabled_plugin_patterns_);
485 ListValueToStringSet(
486 prefs_->GetList(prefs::kPluginsDisabledPluginsExceptions),
487 &policy_disabled_plugin_exception_patterns_);
488 ListValueToStringSet(prefs_->GetList(prefs::kPluginsEnabledPlugins),
489 &policy_enabled_plugin_patterns_);
491 registrar_.Init(prefs_);
493 // Because pointers to our own members will remain unchanged for the
494 // lifetime of |registrar_| (which we also own), we can bind their
495 // pointer values directly in the callbacks to avoid string-based
496 // lookups at notification time.
497 registrar_.Add(prefs::kPluginsDisabledPlugins,
498 base::Bind(&PluginPrefs::UpdatePatternsAndNotify,
499 base::Unretained(this),
500 &policy_disabled_plugin_patterns_));
501 registrar_.Add(prefs::kPluginsDisabledPluginsExceptions,
502 base::Bind(&PluginPrefs::UpdatePatternsAndNotify,
503 base::Unretained(this),
504 &policy_disabled_plugin_exception_patterns_));
505 registrar_.Add(prefs::kPluginsEnabledPlugins,
506 base::Bind(&PluginPrefs::UpdatePatternsAndNotify,
507 base::Unretained(this),
508 &policy_enabled_plugin_patterns_));
510 NotifyPluginStatusChanged();
513 void PluginPrefs::ShutdownOnUIThread() {
514 prefs_ = NULL;
515 registrar_.RemoveAll();
518 PluginPrefs::PluginPrefs() : profile_(NULL),
519 prefs_(NULL) {
522 PluginPrefs::~PluginPrefs() {
525 void PluginPrefs::SetPolicyEnforcedPluginPatterns(
526 const std::set<base::string16>& disabled_patterns,
527 const std::set<base::string16>& disabled_exception_patterns,
528 const std::set<base::string16>& enabled_patterns) {
529 policy_disabled_plugin_patterns_ = disabled_patterns;
530 policy_disabled_plugin_exception_patterns_ = disabled_exception_patterns;
531 policy_enabled_plugin_patterns_ = enabled_patterns;
534 void PluginPrefs::OnUpdatePreferences(
535 const std::vector<content::WebPluginInfo>& plugins) {
536 if (!prefs_)
537 return;
539 PluginFinder* finder = PluginFinder::GetInstance();
540 ListPrefUpdate update(prefs_, prefs::kPluginsPluginsList);
541 base::ListValue* plugins_list = update.Get();
542 plugins_list->Clear();
544 base::FilePath internal_dir;
545 if (PathService::Get(chrome::DIR_INTERNAL_PLUGINS, &internal_dir))
546 prefs_->SetFilePath(prefs::kPluginsLastInternalDirectory, internal_dir);
548 base::AutoLock auto_lock(lock_);
550 // Add the plugin files.
551 std::set<base::string16> group_names;
552 for (size_t i = 0; i < plugins.size(); ++i) {
553 base::DictionaryValue* summary = new base::DictionaryValue();
554 summary->SetString("path", plugins[i].path.value());
555 summary->SetString("name", plugins[i].name);
556 summary->SetString("version", plugins[i].version);
557 bool enabled = true;
558 plugin_state_.Get(plugins[i].path, &enabled);
559 summary->SetBoolean("enabled", enabled);
560 plugins_list->Append(summary);
562 scoped_ptr<PluginMetadata> plugin_metadata(
563 finder->GetPluginMetadata(plugins[i]));
564 // Insert into a set of all group names.
565 group_names.insert(plugin_metadata->name());
568 // Add the plugin groups.
569 for (std::set<base::string16>::const_iterator it = group_names.begin();
570 it != group_names.end(); ++it) {
571 base::DictionaryValue* summary = new base::DictionaryValue();
572 summary->SetString("name", *it);
573 bool enabled = true;
574 std::map<base::string16, bool>::iterator gstate_it =
575 plugin_group_state_.find(*it);
576 if (gstate_it != plugin_group_state_.end())
577 enabled = gstate_it->second;
578 summary->SetBoolean("enabled", enabled);
579 plugins_list->Append(summary);
583 void PluginPrefs::NotifyPluginStatusChanged() {
584 DCHECK_CURRENTLY_ON(BrowserThread::UI);
585 content::NotificationService::current()->Notify(
586 chrome::NOTIFICATION_PLUGIN_ENABLE_STATUS_CHANGED,
587 content::Source<Profile>(profile_),
588 content::NotificationService::NoDetails());