No dual_mode on Win10+ shortcuts.
[chromium-blink-merge.git] / chrome / browser / plugins / plugin_prefs.cc
blobfd58cde5acbba2b4457a0deca77f811ec80187cb
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 } // namespace
62 PluginPrefs::PluginState::PluginState() {
65 PluginPrefs::PluginState::~PluginState() {
68 bool PluginPrefs::PluginState::Get(const base::FilePath& plugin,
69 bool* enabled) const {
70 base::FilePath key = ConvertMapKey(plugin);
71 std::map<base::FilePath, bool>::const_iterator iter = state_.find(key);
72 if (iter != state_.end()) {
73 *enabled = iter->second;
74 return true;
76 return false;
79 void PluginPrefs::PluginState::Set(const base::FilePath& plugin, bool enabled) {
80 state_[ConvertMapKey(plugin)] = enabled;
83 base::FilePath PluginPrefs::PluginState::ConvertMapKey(
84 const base::FilePath& plugin) const {
85 // Keep the state of component-updated and bundled Pepper Flash in sync.
86 if (IsComponentUpdatedPepperFlash(plugin)) {
87 base::FilePath bundled_pepper_flash;
88 if (PathService::Get(chrome::FILE_PEPPER_FLASH_PLUGIN,
89 &bundled_pepper_flash)) {
90 return bundled_pepper_flash;
94 return plugin;
97 // static
98 scoped_refptr<PluginPrefs> PluginPrefs::GetForProfile(Profile* profile) {
99 return PluginPrefsFactory::GetPrefsForProfile(profile);
102 // static
103 scoped_refptr<PluginPrefs> PluginPrefs::GetForTestingProfile(
104 Profile* profile) {
105 return static_cast<PluginPrefs*>(
106 PluginPrefsFactory::GetInstance()->SetTestingFactoryAndUse(
107 profile, &PluginPrefsFactory::CreateForTestingProfile).get());
110 void PluginPrefs::EnablePluginGroup(bool enabled,
111 const base::string16& group_name) {
112 PluginService::GetInstance()->GetPlugins(
113 base::Bind(&PluginPrefs::EnablePluginGroupInternal,
114 this, enabled, group_name));
117 void PluginPrefs::EnablePluginGroupInternal(
118 bool enabled,
119 const base::string16& group_name,
120 const std::vector<content::WebPluginInfo>& plugins) {
121 base::AutoLock auto_lock(lock_);
122 PluginFinder* finder = PluginFinder::GetInstance();
124 // Set the desired state for the group.
125 plugin_group_state_[group_name] = enabled;
127 // Update the state for all plugins in the group.
128 for (size_t i = 0; i < plugins.size(); ++i) {
129 scoped_ptr<PluginMetadata> plugin(finder->GetPluginMetadata(plugins[i]));
130 if (group_name != plugin->name())
131 continue;
132 plugin_state_.Set(plugins[i].path, enabled);
135 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
136 base::Bind(&PluginPrefs::OnUpdatePreferences, this, plugins));
137 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
138 base::Bind(&PluginPrefs::NotifyPluginStatusChanged, this));
141 void PluginPrefs::EnablePlugin(
142 bool enabled, const base::FilePath& path,
143 const base::Callback<void(bool)>& callback) {
144 PluginFinder* finder = PluginFinder::GetInstance();
145 content::WebPluginInfo plugin;
146 bool can_enable = true;
147 if (PluginService::GetInstance()->GetPluginInfoByPath(path, &plugin)) {
148 scoped_ptr<PluginMetadata> plugin_metadata(
149 finder->GetPluginMetadata(plugin));
150 PolicyStatus plugin_status = PolicyStatusForPlugin(plugin.name);
151 PolicyStatus group_status = PolicyStatusForPlugin(plugin_metadata->name());
152 if (enabled) {
153 if (plugin_status == POLICY_DISABLED || group_status == POLICY_DISABLED)
154 can_enable = false;
155 } else {
156 if (plugin_status == POLICY_ENABLED || group_status == POLICY_ENABLED)
157 can_enable = false;
159 } else {
160 NOTREACHED();
163 if (!can_enable) {
164 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
165 base::Bind(callback, false));
166 return;
169 PluginService::GetInstance()->GetPlugins(
170 base::Bind(&PluginPrefs::EnablePluginInternal, this,
171 enabled, path, finder, callback));
174 void PluginPrefs::EnablePluginInternal(
175 bool enabled,
176 const base::FilePath& path,
177 PluginFinder* plugin_finder,
178 const base::Callback<void(bool)>& callback,
179 const std::vector<content::WebPluginInfo>& plugins) {
181 // Set the desired state for the plugin.
182 base::AutoLock auto_lock(lock_);
183 plugin_state_.Set(path, enabled);
186 base::string16 group_name;
187 for (size_t i = 0; i < plugins.size(); ++i) {
188 if (plugins[i].path == path) {
189 scoped_ptr<PluginMetadata> plugin_metadata(
190 plugin_finder->GetPluginMetadata(plugins[i]));
191 // set the group name for this plugin.
192 group_name = plugin_metadata->name();
193 DCHECK_EQ(enabled, IsPluginEnabled(plugins[i]));
194 break;
198 bool all_disabled = true;
199 for (size_t i = 0; i < plugins.size(); ++i) {
200 scoped_ptr<PluginMetadata> plugin_metadata(
201 plugin_finder->GetPluginMetadata(plugins[i]));
202 DCHECK(!plugin_metadata->name().empty());
203 if (group_name == plugin_metadata->name()) {
204 all_disabled = all_disabled && !IsPluginEnabled(plugins[i]);
208 if (!group_name.empty()) {
209 // Update the state for the corresponding plugin group.
210 base::AutoLock auto_lock(lock_);
211 plugin_group_state_[group_name] = !all_disabled;
214 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
215 base::Bind(&PluginPrefs::OnUpdatePreferences, this, plugins));
216 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
217 base::Bind(&PluginPrefs::NotifyPluginStatusChanged, this));
218 callback.Run(true);
221 PluginPrefs::PolicyStatus PluginPrefs::PolicyStatusForPlugin(
222 const base::string16& name) const {
223 base::AutoLock auto_lock(lock_);
224 if (IsStringMatchedInSet(name, policy_enabled_plugin_patterns_)) {
225 return POLICY_ENABLED;
226 } else if (IsStringMatchedInSet(name, policy_disabled_plugin_patterns_) &&
227 !IsStringMatchedInSet(
228 name, policy_disabled_plugin_exception_patterns_)) {
229 return POLICY_DISABLED;
230 } else {
231 return NO_POLICY;
235 bool PluginPrefs::IsPluginEnabled(const content::WebPluginInfo& plugin) const {
236 scoped_ptr<PluginMetadata> plugin_metadata(
237 PluginFinder::GetInstance()->GetPluginMetadata(plugin));
238 base::string16 group_name = plugin_metadata->name();
240 // Check if the plugin or its group is enabled by policy.
241 PolicyStatus plugin_status = PolicyStatusForPlugin(plugin.name);
242 PolicyStatus group_status = PolicyStatusForPlugin(group_name);
243 if (plugin_status == POLICY_ENABLED || group_status == POLICY_ENABLED)
244 return true;
246 // Check if the plugin or its group is disabled by policy.
247 if (plugin_status == POLICY_DISABLED || group_status == POLICY_DISABLED)
248 return false;
250 #if !defined(DISABLE_NACL)
251 // If enabling NaCl, make sure the plugin is also enabled. See bug
252 // http://code.google.com/p/chromium/issues/detail?id=81010 for more
253 // information.
254 // TODO(dspringer): When NaCl is on by default, remove this code.
255 if ((plugin.name == base::ASCIIToUTF16(nacl::kNaClPluginName)) &&
256 base::CommandLine::ForCurrentProcess()->HasSwitch(
257 switches::kEnableNaCl)) {
258 return true;
260 #endif
262 base::AutoLock auto_lock(lock_);
263 // Check user preferences for the plugin.
264 bool plugin_enabled = false;
265 if (plugin_state_.Get(plugin.path, &plugin_enabled))
266 return plugin_enabled;
268 // Check user preferences for the plugin group.
269 std::map<base::string16, bool>::const_iterator group_it(
270 plugin_group_state_.find(group_name));
271 if (group_it != plugin_group_state_.end())
272 return group_it->second;
274 // Default to enabled.
275 return true;
278 void PluginPrefs::UpdatePatternsAndNotify(std::set<base::string16>* patterns,
279 const std::string& pref_name) {
280 base::AutoLock auto_lock(lock_);
281 ListValueToStringSet(prefs_->GetList(pref_name.c_str()), patterns);
283 NotifyPluginStatusChanged();
286 /*static*/
287 bool PluginPrefs::IsStringMatchedInSet(
288 const base::string16& name,
289 const std::set<base::string16>& pattern_set) {
290 std::set<base::string16>::const_iterator pattern(pattern_set.begin());
291 while (pattern != pattern_set.end()) {
292 if (base::MatchPattern(name, *pattern))
293 return true;
294 ++pattern;
297 return false;
300 /* static */
301 void PluginPrefs::ListValueToStringSet(const base::ListValue* src,
302 std::set<base::string16>* dest) {
303 DCHECK(src);
304 DCHECK(dest);
305 dest->clear();
306 base::ListValue::const_iterator end(src->end());
307 for (base::ListValue::const_iterator current(src->begin());
308 current != end; ++current) {
309 base::string16 plugin_name;
310 if ((*current)->GetAsString(&plugin_name)) {
311 dest->insert(plugin_name);
316 void PluginPrefs::SetPrefs(PrefService* prefs) {
317 prefs_ = prefs;
318 bool update_internal_dir = false;
319 base::FilePath last_internal_dir =
320 prefs_->GetFilePath(prefs::kPluginsLastInternalDirectory);
321 base::FilePath cur_internal_dir;
322 if (PathService::Get(chrome::DIR_INTERNAL_PLUGINS, &cur_internal_dir) &&
323 cur_internal_dir != last_internal_dir) {
324 update_internal_dir = true;
325 prefs_->SetFilePath(
326 prefs::kPluginsLastInternalDirectory, cur_internal_dir);
329 bool migrate_to_pepper_flash = false;
330 #if defined(OS_WIN) || defined(OS_MACOSX)
331 // If bundled NPAPI Flash is enabled while Pepper Flash is disabled, we
332 // would like to turn Pepper Flash on. And we only want to do it once.
333 // TODO(yzshen): Remove all |migrate_to_pepper_flash|-related code after it
334 // has been run once by most users. (Maybe Chrome 24 or Chrome 25.)
335 // NOTE(shess): Keep in mind that Mac is on a different schedule.
336 if (!prefs_->GetBoolean(prefs::kPluginsMigratedToPepperFlash)) {
337 prefs_->SetBoolean(prefs::kPluginsMigratedToPepperFlash, true);
338 migrate_to_pepper_flash = true;
340 #endif
342 bool remove_component_pepper_flash_settings = false;
343 // If component-updated Pepper Flash is disabled, we would like to remove that
344 // settings item. And we only want to do it once. (Please see the comments of
345 // kPluginsRemovedOldComponentPepperFlashSettings for why.)
346 // TODO(yzshen): Remove all |remove_component_pepper_flash_settings|-related
347 // code after it has been run once by most users.
348 if (!prefs_->GetBoolean(
349 prefs::kPluginsRemovedOldComponentPepperFlashSettings)) {
350 prefs_->SetBoolean(prefs::kPluginsRemovedOldComponentPepperFlashSettings,
351 true);
352 remove_component_pepper_flash_settings = true;
355 { // Scoped update of prefs::kPluginsPluginsList.
356 ListPrefUpdate update(prefs_, prefs::kPluginsPluginsList);
357 base::ListValue* saved_plugins_list = update.Get();
358 if (saved_plugins_list && !saved_plugins_list->empty()) {
359 // The following four variables are only valid when
360 // |migrate_to_pepper_flash| is set to true.
361 base::FilePath npapi_flash;
362 base::FilePath pepper_flash;
363 base::DictionaryValue* pepper_flash_node = NULL;
364 bool npapi_flash_enabled = false;
365 if (migrate_to_pepper_flash) {
366 PathService::Get(chrome::FILE_FLASH_PLUGIN, &npapi_flash);
367 PathService::Get(chrome::FILE_PEPPER_FLASH_PLUGIN, &pepper_flash);
370 // Used when |remove_component_pepper_flash_settings| is set to true.
371 base::ListValue::iterator component_pepper_flash_node =
372 saved_plugins_list->end();
374 for (base::ListValue::iterator it = saved_plugins_list->begin();
375 it != saved_plugins_list->end();
376 ++it) {
377 if (!(*it)->IsType(base::Value::TYPE_DICTIONARY)) {
378 LOG(WARNING) << "Invalid entry in " << prefs::kPluginsPluginsList;
379 continue; // Oops, don't know what to do with this item.
382 base::DictionaryValue* plugin =
383 static_cast<base::DictionaryValue*>(*it);
384 base::string16 group_name;
385 bool enabled;
386 if (!plugin->GetBoolean("enabled", &enabled))
387 enabled = true;
389 base::FilePath::StringType path;
390 // The plugin list constains all the plugin files in addition to the
391 // plugin groups.
392 if (plugin->GetString("path", &path)) {
393 // Files have a path attribute, groups don't.
394 base::FilePath plugin_path(path);
396 // The path to the intenral plugin directory changes everytime Chrome
397 // is auto-updated, since it contains the current version number. For
398 // example, it changes from foobar\Chrome\Application\21.0.1180.83 to
399 // foobar\Chrome\Application\21.0.1180.89.
400 // However, we would like the settings of internal plugins to persist
401 // across Chrome updates. Therefore, we need to recognize those paths
402 // that are within the previous internal plugin directory, and update
403 // them in the prefs accordingly.
404 if (update_internal_dir) {
405 base::FilePath relative_path;
407 // Extract the part of |plugin_path| that is relative to
408 // |last_internal_dir|. For example, |relative_path| will be
409 // foo\bar.dll if |plugin_path| is <last_internal_dir>\foo\bar.dll.
411 // Every iteration the last path component from |plugin_path| is
412 // removed and prepended to |relative_path| until we get up to
413 // |last_internal_dir|.
414 while (last_internal_dir.IsParent(plugin_path)) {
415 relative_path = plugin_path.BaseName().Append(relative_path);
417 base::FilePath old_path = plugin_path;
418 plugin_path = plugin_path.DirName();
419 // To be extra sure that we won't end up in an infinite loop.
420 if (old_path == plugin_path) {
421 NOTREACHED();
422 break;
426 // If |relative_path| is empty, |plugin_path| is not within
427 // |last_internal_dir|. We don't need to update it.
428 if (!relative_path.empty()) {
429 plugin_path = cur_internal_dir.Append(relative_path);
430 path = plugin_path.value();
431 plugin->SetString("path", path);
435 if (migrate_to_pepper_flash &&
436 base::FilePath::CompareEqualIgnoreCase(
437 path, npapi_flash.value())) {
438 npapi_flash_enabled = enabled;
439 } else if (migrate_to_pepper_flash &&
440 base::FilePath::CompareEqualIgnoreCase(
441 path, pepper_flash.value())) {
442 if (!enabled)
443 pepper_flash_node = plugin;
444 } else if (remove_component_pepper_flash_settings &&
445 IsComponentUpdatedPepperFlash(plugin_path)) {
446 if (!enabled) {
447 component_pepper_flash_node = it;
448 // Skip setting |enabled| into |plugin_state_|.
449 continue;
453 plugin_state_.Set(plugin_path, enabled);
454 } else if (!enabled && plugin->GetString("name", &group_name)) {
455 // Otherwise this is a list of groups.
456 plugin_group_state_[group_name] = false;
460 if (npapi_flash_enabled && pepper_flash_node) {
461 DCHECK(migrate_to_pepper_flash);
462 pepper_flash_node->SetBoolean("enabled", true);
463 plugin_state_.Set(pepper_flash, true);
466 if (component_pepper_flash_node != saved_plugins_list->end()) {
467 DCHECK(remove_component_pepper_flash_settings);
468 saved_plugins_list->Erase(component_pepper_flash_node, NULL);
470 } else {
471 // If the saved plugin list is empty, then the call to UpdatePreferences()
472 // below failed in an earlier run, possibly because the user closed the
473 // browser too quickly.
475 // Only want one PDF plugin enabled at a time. See http://crbug.com/50105
476 // for background.
477 plugin_group_state_[base::ASCIIToUTF16(
478 PluginMetadata::kAdobeReaderGroupName)] = false;
480 } // Scoped update of prefs::kPluginsPluginsList.
482 // Build the set of policy enabled/disabled plugin patterns once and cache it.
483 // Don't do this in the constructor, there's no profile available there.
484 ListValueToStringSet(prefs_->GetList(prefs::kPluginsDisabledPlugins),
485 &policy_disabled_plugin_patterns_);
486 ListValueToStringSet(
487 prefs_->GetList(prefs::kPluginsDisabledPluginsExceptions),
488 &policy_disabled_plugin_exception_patterns_);
489 ListValueToStringSet(prefs_->GetList(prefs::kPluginsEnabledPlugins),
490 &policy_enabled_plugin_patterns_);
492 registrar_.Init(prefs_);
494 // Because pointers to our own members will remain unchanged for the
495 // lifetime of |registrar_| (which we also own), we can bind their
496 // pointer values directly in the callbacks to avoid string-based
497 // lookups at notification time.
498 registrar_.Add(prefs::kPluginsDisabledPlugins,
499 base::Bind(&PluginPrefs::UpdatePatternsAndNotify,
500 base::Unretained(this),
501 &policy_disabled_plugin_patterns_));
502 registrar_.Add(prefs::kPluginsDisabledPluginsExceptions,
503 base::Bind(&PluginPrefs::UpdatePatternsAndNotify,
504 base::Unretained(this),
505 &policy_disabled_plugin_exception_patterns_));
506 registrar_.Add(prefs::kPluginsEnabledPlugins,
507 base::Bind(&PluginPrefs::UpdatePatternsAndNotify,
508 base::Unretained(this),
509 &policy_enabled_plugin_patterns_));
511 NotifyPluginStatusChanged();
514 void PluginPrefs::ShutdownOnUIThread() {
515 prefs_ = NULL;
516 registrar_.RemoveAll();
519 PluginPrefs::PluginPrefs() : profile_(NULL),
520 prefs_(NULL) {
523 PluginPrefs::~PluginPrefs() {
526 void PluginPrefs::SetPolicyEnforcedPluginPatterns(
527 const std::set<base::string16>& disabled_patterns,
528 const std::set<base::string16>& disabled_exception_patterns,
529 const std::set<base::string16>& enabled_patterns) {
530 policy_disabled_plugin_patterns_ = disabled_patterns;
531 policy_disabled_plugin_exception_patterns_ = disabled_exception_patterns;
532 policy_enabled_plugin_patterns_ = enabled_patterns;
535 void PluginPrefs::OnUpdatePreferences(
536 const std::vector<content::WebPluginInfo>& plugins) {
537 if (!prefs_)
538 return;
540 PluginFinder* finder = PluginFinder::GetInstance();
541 ListPrefUpdate update(prefs_, prefs::kPluginsPluginsList);
542 base::ListValue* plugins_list = update.Get();
543 plugins_list->Clear();
545 base::FilePath internal_dir;
546 if (PathService::Get(chrome::DIR_INTERNAL_PLUGINS, &internal_dir))
547 prefs_->SetFilePath(prefs::kPluginsLastInternalDirectory, internal_dir);
549 base::AutoLock auto_lock(lock_);
551 // Add the plugin files.
552 std::set<base::string16> group_names;
553 for (size_t i = 0; i < plugins.size(); ++i) {
554 base::DictionaryValue* summary = new base::DictionaryValue();
555 summary->SetString("path", plugins[i].path.value());
556 summary->SetString("name", plugins[i].name);
557 summary->SetString("version", plugins[i].version);
558 bool enabled = true;
559 plugin_state_.Get(plugins[i].path, &enabled);
560 summary->SetBoolean("enabled", enabled);
561 plugins_list->Append(summary);
563 scoped_ptr<PluginMetadata> plugin_metadata(
564 finder->GetPluginMetadata(plugins[i]));
565 // Insert into a set of all group names.
566 group_names.insert(plugin_metadata->name());
569 // Add the plugin groups.
570 for (std::set<base::string16>::const_iterator it = group_names.begin();
571 it != group_names.end(); ++it) {
572 base::DictionaryValue* summary = new base::DictionaryValue();
573 summary->SetString("name", *it);
574 bool enabled = true;
575 std::map<base::string16, bool>::iterator gstate_it =
576 plugin_group_state_.find(*it);
577 if (gstate_it != plugin_group_state_.end())
578 enabled = gstate_it->second;
579 summary->SetBoolean("enabled", enabled);
580 plugins_list->Append(summary);
584 void PluginPrefs::NotifyPluginStatusChanged() {
585 DCHECK_CURRENTLY_ON(BrowserThread::UI);
586 content::NotificationService::current()->Notify(
587 chrome::NOTIFICATION_PLUGIN_ENABLE_STATUS_CHANGED,
588 content::Source<Profile>(profile_),
589 content::NotificationService::NoDetails());