Popular sites on the NTP: check that experiment group StartsWith (rather than IS...
[chromium-blink-merge.git] / chrome / browser / plugins / plugin_prefs.cc
bloba754c327b34998c1d22a6f672bfb80ab98d896a7
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/pattern.h"
17 #include "base/strings/string_util.h"
18 #include "base/strings/utf_string_conversions.h"
19 #include "base/thread_task_runner_handle.h"
20 #include "base/values.h"
21 #include "build/build_config.h"
22 #include "chrome/browser/browser_process.h"
23 #include "chrome/browser/chrome_notification_types.h"
24 #include "chrome/browser/plugins/plugin_installer.h"
25 #include "chrome/browser/plugins/plugin_metadata.h"
26 #include "chrome/browser/plugins/plugin_prefs_factory.h"
27 #include "chrome/browser/profiles/profile.h"
28 #include "chrome/common/chrome_constants.h"
29 #include "chrome/common/chrome_paths.h"
30 #include "chrome/common/chrome_switches.h"
31 #include "chrome/common/pref_names.h"
32 #include "components/keyed_service/core/keyed_service.h"
33 #include "content/public/browser/browser_thread.h"
34 #include "content/public/browser/notification_service.h"
35 #include "content/public/browser/plugin_service.h"
36 #include "content/public/common/webplugininfo.h"
38 #if !defined(DISABLE_NACL)
39 #include "components/nacl/common/nacl_constants.h"
40 #endif
42 using content::BrowserThread;
43 using content::PluginService;
45 namespace {
47 bool IsComponentUpdatedPepperFlash(const base::FilePath& plugin) {
48 if (plugin.BaseName().value() == chrome::kPepperFlashPluginFilename) {
49 base::FilePath component_updated_pepper_flash_dir;
50 if (PathService::Get(chrome::DIR_COMPONENT_UPDATED_PEPPER_FLASH_PLUGIN,
51 &component_updated_pepper_flash_dir) &&
52 component_updated_pepper_flash_dir.IsParent(plugin)) {
53 return true;
57 return false;
60 // Returns true if |path| looks like the path to an NPAPI Flash plugin.
61 bool IsNpapiFlashPath(const base::FilePath& path) {
62 base::FilePath npapi_flash;
63 // Check NPAPI Flash is installed.
64 if (!PathService::Get(chrome::FILE_FLASH_SYSTEM_PLUGIN, &npapi_flash))
65 return false;
66 // Check for same architecture NPAPI Flash.
67 if (base::FilePath::CompareEqualIgnoreCase(path.value(), npapi_flash.value()))
68 return true;
69 #if defined(OS_WIN)
70 // Fuzzy check for NPAPI Flash on Windows.
71 base::FilePath::StringType kSwfPrefix = FILE_PATH_LITERAL("NPSWF");
72 if (path.BaseName().value().compare(0, kSwfPrefix.size(), kSwfPrefix) == 0)
73 return true;
74 #endif
75 return false;
78 } // namespace
80 PluginPrefs::PluginState::PluginState() {
83 PluginPrefs::PluginState::~PluginState() {
86 bool PluginPrefs::PluginState::Get(const base::FilePath& plugin,
87 bool* enabled) const {
88 base::FilePath key = ConvertMapKey(plugin);
89 std::map<base::FilePath, bool>::const_iterator iter = state_.find(key);
90 if (iter != state_.end()) {
91 *enabled = iter->second;
92 return true;
94 return false;
97 void PluginPrefs::PluginState::Set(const base::FilePath& plugin, bool enabled) {
98 state_[ConvertMapKey(plugin)] = enabled;
101 base::FilePath PluginPrefs::PluginState::ConvertMapKey(
102 const base::FilePath& plugin) const {
103 // Keep the state of component-updated and bundled Pepper Flash in sync.
104 if (IsComponentUpdatedPepperFlash(plugin)) {
105 base::FilePath bundled_pepper_flash;
106 if (PathService::Get(chrome::FILE_PEPPER_FLASH_PLUGIN,
107 &bundled_pepper_flash)) {
108 return bundled_pepper_flash;
112 return plugin;
115 // static
116 scoped_refptr<PluginPrefs> PluginPrefs::GetForProfile(Profile* profile) {
117 return PluginPrefsFactory::GetPrefsForProfile(profile);
120 // static
121 scoped_refptr<PluginPrefs> PluginPrefs::GetForTestingProfile(
122 Profile* profile) {
123 return static_cast<PluginPrefs*>(
124 PluginPrefsFactory::GetInstance()->SetTestingFactoryAndUse(
125 profile, &PluginPrefsFactory::CreateForTestingProfile).get());
128 void PluginPrefs::EnablePluginGroup(bool enabled,
129 const base::string16& group_name) {
130 PluginService::GetInstance()->GetPlugins(
131 base::Bind(&PluginPrefs::EnablePluginGroupInternal,
132 this, enabled, group_name));
135 void PluginPrefs::EnablePluginGroupInternal(
136 bool enabled,
137 const base::string16& group_name,
138 const std::vector<content::WebPluginInfo>& plugins) {
139 base::AutoLock auto_lock(lock_);
140 PluginFinder* finder = PluginFinder::GetInstance();
142 // Set the desired state for the group.
143 plugin_group_state_[group_name] = enabled;
145 // Update the state for all plugins in the group.
146 for (size_t i = 0; i < plugins.size(); ++i) {
147 scoped_ptr<PluginMetadata> plugin(finder->GetPluginMetadata(plugins[i]));
148 if (group_name != plugin->name())
149 continue;
150 plugin_state_.Set(plugins[i].path, enabled);
153 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
154 base::Bind(&PluginPrefs::OnUpdatePreferences, this, plugins));
155 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
156 base::Bind(&PluginPrefs::NotifyPluginStatusChanged, this));
159 void PluginPrefs::EnablePlugin(
160 bool enabled, const base::FilePath& path,
161 const base::Callback<void(bool)>& callback) {
162 PluginFinder* finder = PluginFinder::GetInstance();
163 content::WebPluginInfo plugin;
164 bool can_enable = true;
165 if (PluginService::GetInstance()->GetPluginInfoByPath(path, &plugin)) {
166 scoped_ptr<PluginMetadata> plugin_metadata(
167 finder->GetPluginMetadata(plugin));
168 PolicyStatus plugin_status = PolicyStatusForPlugin(plugin.name);
169 PolicyStatus group_status = PolicyStatusForPlugin(plugin_metadata->name());
170 if (enabled) {
171 if (plugin_status == POLICY_DISABLED || group_status == POLICY_DISABLED)
172 can_enable = false;
173 } else {
174 if (plugin_status == POLICY_ENABLED || group_status == POLICY_ENABLED)
175 can_enable = false;
177 } else {
178 NOTREACHED();
181 if (!can_enable) {
182 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
183 base::Bind(callback, false));
184 return;
187 PluginService::GetInstance()->GetPlugins(
188 base::Bind(&PluginPrefs::EnablePluginInternal, this,
189 enabled, path, finder, callback));
192 void PluginPrefs::EnablePluginInternal(
193 bool enabled,
194 const base::FilePath& path,
195 PluginFinder* plugin_finder,
196 const base::Callback<void(bool)>& callback,
197 const std::vector<content::WebPluginInfo>& plugins) {
199 // Set the desired state for the plugin.
200 base::AutoLock auto_lock(lock_);
201 plugin_state_.Set(path, enabled);
204 base::string16 group_name;
205 for (size_t i = 0; i < plugins.size(); ++i) {
206 if (plugins[i].path == path) {
207 scoped_ptr<PluginMetadata> plugin_metadata(
208 plugin_finder->GetPluginMetadata(plugins[i]));
209 // set the group name for this plugin.
210 group_name = plugin_metadata->name();
211 DCHECK_EQ(enabled, IsPluginEnabled(plugins[i]));
212 break;
216 bool all_disabled = true;
217 for (size_t i = 0; i < plugins.size(); ++i) {
218 scoped_ptr<PluginMetadata> plugin_metadata(
219 plugin_finder->GetPluginMetadata(plugins[i]));
220 DCHECK(!plugin_metadata->name().empty());
221 if (group_name == plugin_metadata->name()) {
222 all_disabled = all_disabled && !IsPluginEnabled(plugins[i]);
226 if (!group_name.empty()) {
227 // Update the state for the corresponding plugin group.
228 base::AutoLock auto_lock(lock_);
229 plugin_group_state_[group_name] = !all_disabled;
232 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
233 base::Bind(&PluginPrefs::OnUpdatePreferences, this, plugins));
234 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
235 base::Bind(&PluginPrefs::NotifyPluginStatusChanged, this));
236 callback.Run(true);
239 PluginPrefs::PolicyStatus PluginPrefs::PolicyStatusForPlugin(
240 const base::string16& name) const {
241 base::AutoLock auto_lock(lock_);
242 if (IsStringMatchedInSet(name, policy_enabled_plugin_patterns_)) {
243 return POLICY_ENABLED;
244 } else if (IsStringMatchedInSet(name, policy_disabled_plugin_patterns_) &&
245 !IsStringMatchedInSet(
246 name, policy_disabled_plugin_exception_patterns_)) {
247 return POLICY_DISABLED;
248 } else {
249 return NO_POLICY;
253 bool PluginPrefs::IsPluginEnabled(const content::WebPluginInfo& plugin) const {
254 scoped_ptr<PluginMetadata> plugin_metadata(
255 PluginFinder::GetInstance()->GetPluginMetadata(plugin));
256 base::string16 group_name = plugin_metadata->name();
258 // Check if the plugin or its group is enabled by policy.
259 PolicyStatus plugin_status = PolicyStatusForPlugin(plugin.name);
260 PolicyStatus group_status = PolicyStatusForPlugin(group_name);
261 if (plugin_status == POLICY_ENABLED || group_status == POLICY_ENABLED)
262 return true;
264 // Check if the plugin or its group is disabled by policy.
265 if (plugin_status == POLICY_DISABLED || group_status == POLICY_DISABLED)
266 return false;
268 #if !defined(DISABLE_NACL)
269 // If enabling NaCl, make sure the plugin is also enabled. See bug
270 // http://code.google.com/p/chromium/issues/detail?id=81010 for more
271 // information.
272 // TODO(dspringer): When NaCl is on by default, remove this code.
273 if ((plugin.name == base::ASCIIToUTF16(nacl::kNaClPluginName)) &&
274 base::CommandLine::ForCurrentProcess()->HasSwitch(
275 switches::kEnableNaCl)) {
276 return true;
278 #endif
280 base::AutoLock auto_lock(lock_);
281 // Check user preferences for the plugin.
282 bool plugin_enabled = false;
283 if (plugin_state_.Get(plugin.path, &plugin_enabled))
284 return plugin_enabled;
286 // Check user preferences for the plugin group.
287 std::map<base::string16, bool>::const_iterator group_it(
288 plugin_group_state_.find(group_name));
289 if (group_it != plugin_group_state_.end())
290 return group_it->second;
292 // Default to enabled.
293 return true;
296 void PluginPrefs::UpdatePatternsAndNotify(std::set<base::string16>* patterns,
297 const std::string& pref_name) {
298 base::AutoLock auto_lock(lock_);
299 ListValueToStringSet(prefs_->GetList(pref_name.c_str()), patterns);
301 NotifyPluginStatusChanged();
304 /*static*/
305 bool PluginPrefs::IsStringMatchedInSet(
306 const base::string16& name,
307 const std::set<base::string16>& pattern_set) {
308 std::set<base::string16>::const_iterator pattern(pattern_set.begin());
309 while (pattern != pattern_set.end()) {
310 if (base::MatchPattern(name, *pattern))
311 return true;
312 ++pattern;
315 return false;
318 /* static */
319 void PluginPrefs::ListValueToStringSet(const base::ListValue* src,
320 std::set<base::string16>* dest) {
321 DCHECK(src);
322 DCHECK(dest);
323 dest->clear();
324 base::ListValue::const_iterator end(src->end());
325 for (base::ListValue::const_iterator current(src->begin());
326 current != end; ++current) {
327 base::string16 plugin_name;
328 if ((*current)->GetAsString(&plugin_name)) {
329 dest->insert(plugin_name);
334 void PluginPrefs::SetPrefs(PrefService* prefs) {
335 prefs_ = prefs;
336 bool update_internal_dir = false;
337 base::FilePath last_internal_dir =
338 prefs_->GetFilePath(prefs::kPluginsLastInternalDirectory);
339 base::FilePath cur_internal_dir;
340 if (PathService::Get(chrome::DIR_INTERNAL_PLUGINS, &cur_internal_dir) &&
341 cur_internal_dir != last_internal_dir) {
342 update_internal_dir = true;
343 prefs_->SetFilePath(
344 prefs::kPluginsLastInternalDirectory, cur_internal_dir);
347 bool migrate_to_pepper_flash = false;
348 #if defined(OS_WIN) || defined(OS_MACOSX)
349 // If NPAPI Flash is enabled while Pepper Flash is disabled, we would like to
350 // turn Pepper Flash on. And we want to do it once, when NPAPI is disabled in
351 // Chrome 45.
352 // TODO(wfh): Remove this code once it has been run by most users, around
353 // Chrome 49 or Chrome 50. See crbug.com/514250.
354 if (!prefs_->GetBoolean(prefs::kNpapiFlashMigratedToPepperFlash)) {
355 prefs_->SetBoolean(prefs::kNpapiFlashMigratedToPepperFlash, true);
356 migrate_to_pepper_flash = true;
358 #endif
360 { // Scoped update of prefs::kPluginsPluginsList.
361 ListPrefUpdate update(prefs_, prefs::kPluginsPluginsList);
362 base::ListValue* saved_plugins_list = update.Get();
363 if (saved_plugins_list && !saved_plugins_list->empty()) {
364 // The following four variables are only valid when
365 // |migrate_to_pepper_flash| is set to true.
366 base::FilePath pepper_flash;
367 base::DictionaryValue* pepper_flash_node = NULL;
368 bool npapi_flash_enabled = false;
369 if (migrate_to_pepper_flash) {
370 PathService::Get(chrome::FILE_PEPPER_FLASH_PLUGIN, &pepper_flash);
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 internal 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 && IsNpapiFlashPath(plugin_path)) {
435 npapi_flash_enabled = enabled;
436 } else if (migrate_to_pepper_flash &&
437 base::FilePath::CompareEqualIgnoreCase(
438 path, pepper_flash.value())) {
439 if (!enabled)
440 pepper_flash_node = plugin;
443 plugin_state_.Set(plugin_path, enabled);
444 } else if (!enabled && plugin->GetString("name", &group_name)) {
445 // Otherwise this is a list of groups.
446 plugin_group_state_[group_name] = false;
450 if (npapi_flash_enabled && pepper_flash_node) {
451 DCHECK(migrate_to_pepper_flash);
452 pepper_flash_node->SetBoolean("enabled", true);
453 plugin_state_.Set(pepper_flash, true);
455 } else {
456 // If the saved plugin list is empty, then the call to UpdatePreferences()
457 // below failed in an earlier run, possibly because the user closed the
458 // browser too quickly.
460 // Only want one PDF plugin enabled at a time. See http://crbug.com/50105
461 // for background.
462 plugin_group_state_[base::ASCIIToUTF16(
463 PluginMetadata::kAdobeReaderGroupName)] = false;
465 } // Scoped update of prefs::kPluginsPluginsList.
467 // Build the set of policy enabled/disabled plugin patterns once and cache it.
468 // Don't do this in the constructor, there's no profile available there.
469 ListValueToStringSet(prefs_->GetList(prefs::kPluginsDisabledPlugins),
470 &policy_disabled_plugin_patterns_);
471 ListValueToStringSet(
472 prefs_->GetList(prefs::kPluginsDisabledPluginsExceptions),
473 &policy_disabled_plugin_exception_patterns_);
474 ListValueToStringSet(prefs_->GetList(prefs::kPluginsEnabledPlugins),
475 &policy_enabled_plugin_patterns_);
477 registrar_.Init(prefs_);
479 // Because pointers to our own members will remain unchanged for the
480 // lifetime of |registrar_| (which we also own), we can bind their
481 // pointer values directly in the callbacks to avoid string-based
482 // lookups at notification time.
483 registrar_.Add(prefs::kPluginsDisabledPlugins,
484 base::Bind(&PluginPrefs::UpdatePatternsAndNotify,
485 base::Unretained(this),
486 &policy_disabled_plugin_patterns_));
487 registrar_.Add(prefs::kPluginsDisabledPluginsExceptions,
488 base::Bind(&PluginPrefs::UpdatePatternsAndNotify,
489 base::Unretained(this),
490 &policy_disabled_plugin_exception_patterns_));
491 registrar_.Add(prefs::kPluginsEnabledPlugins,
492 base::Bind(&PluginPrefs::UpdatePatternsAndNotify,
493 base::Unretained(this),
494 &policy_enabled_plugin_patterns_));
496 NotifyPluginStatusChanged();
499 void PluginPrefs::ShutdownOnUIThread() {
500 prefs_ = NULL;
501 registrar_.RemoveAll();
504 PluginPrefs::PluginPrefs() : profile_(NULL),
505 prefs_(NULL) {
508 PluginPrefs::~PluginPrefs() {
511 void PluginPrefs::SetPolicyEnforcedPluginPatterns(
512 const std::set<base::string16>& disabled_patterns,
513 const std::set<base::string16>& disabled_exception_patterns,
514 const std::set<base::string16>& enabled_patterns) {
515 policy_disabled_plugin_patterns_ = disabled_patterns;
516 policy_disabled_plugin_exception_patterns_ = disabled_exception_patterns;
517 policy_enabled_plugin_patterns_ = enabled_patterns;
520 void PluginPrefs::OnUpdatePreferences(
521 const std::vector<content::WebPluginInfo>& plugins) {
522 if (!prefs_)
523 return;
525 PluginFinder* finder = PluginFinder::GetInstance();
526 ListPrefUpdate update(prefs_, prefs::kPluginsPluginsList);
527 base::ListValue* plugins_list = update.Get();
528 plugins_list->Clear();
530 base::FilePath internal_dir;
531 if (PathService::Get(chrome::DIR_INTERNAL_PLUGINS, &internal_dir))
532 prefs_->SetFilePath(prefs::kPluginsLastInternalDirectory, internal_dir);
534 base::AutoLock auto_lock(lock_);
536 // Add the plugin files.
537 std::set<base::string16> group_names;
538 for (size_t i = 0; i < plugins.size(); ++i) {
539 base::DictionaryValue* summary = new base::DictionaryValue();
540 summary->SetString("path", plugins[i].path.value());
541 summary->SetString("name", plugins[i].name);
542 summary->SetString("version", plugins[i].version);
543 bool enabled = true;
544 plugin_state_.Get(plugins[i].path, &enabled);
545 summary->SetBoolean("enabled", enabled);
546 plugins_list->Append(summary);
548 scoped_ptr<PluginMetadata> plugin_metadata(
549 finder->GetPluginMetadata(plugins[i]));
550 // Insert into a set of all group names.
551 group_names.insert(plugin_metadata->name());
554 // Add the plugin groups.
555 for (std::set<base::string16>::const_iterator it = group_names.begin();
556 it != group_names.end(); ++it) {
557 base::DictionaryValue* summary = new base::DictionaryValue();
558 summary->SetString("name", *it);
559 bool enabled = true;
560 std::map<base::string16, bool>::iterator gstate_it =
561 plugin_group_state_.find(*it);
562 if (gstate_it != plugin_group_state_.end())
563 enabled = gstate_it->second;
564 summary->SetBoolean("enabled", enabled);
565 plugins_list->Append(summary);
569 void PluginPrefs::NotifyPluginStatusChanged() {
570 DCHECK_CURRENTLY_ON(BrowserThread::UI);
571 content::NotificationService::current()->Notify(
572 chrome::NOTIFICATION_PLUGIN_ENABLE_STATUS_CHANGED,
573 content::Source<Profile>(profile_),
574 content::NotificationService::NoDetails());