Check USB device path access when prompting users to select a device.
[chromium-blink-merge.git] / chrome / browser / plugins / plugin_prefs.cc
blob97dcf993ea7efe82fd659b7221d5a93f9726edfd
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/memory/scoped_ptr.h"
12 #include "base/message_loop/message_loop.h"
13 #include "base/path_service.h"
14 #include "base/prefs/scoped_user_pref_update.h"
15 #include "base/strings/string_util.h"
16 #include "base/strings/utf_string_conversions.h"
17 #include "base/values.h"
18 #include "build/build_config.h"
19 #include "chrome/browser/browser_process.h"
20 #include "chrome/browser/chrome_notification_types.h"
21 #include "chrome/browser/plugins/plugin_installer.h"
22 #include "chrome/browser/plugins/plugin_metadata.h"
23 #include "chrome/browser/plugins/plugin_prefs_factory.h"
24 #include "chrome/browser/profiles/profile.h"
25 #include "chrome/common/chrome_constants.h"
26 #include "chrome/common/chrome_paths.h"
27 #include "chrome/common/chrome_switches.h"
28 #include "chrome/common/pref_names.h"
29 #include "components/keyed_service/core/keyed_service.h"
30 #include "content/public/browser/browser_thread.h"
31 #include "content/public/browser/notification_service.h"
32 #include "content/public/browser/plugin_service.h"
33 #include "content/public/common/webplugininfo.h"
35 #if !defined(DISABLE_NACL)
36 #include "components/nacl/common/nacl_constants.h"
37 #endif
39 using content::BrowserThread;
40 using content::PluginService;
42 namespace {
44 bool IsComponentUpdatedPepperFlash(const base::FilePath& plugin) {
45 if (plugin.BaseName().value() == chrome::kPepperFlashPluginFilename) {
46 base::FilePath component_updated_pepper_flash_dir;
47 if (PathService::Get(chrome::DIR_COMPONENT_UPDATED_PEPPER_FLASH_PLUGIN,
48 &component_updated_pepper_flash_dir) &&
49 component_updated_pepper_flash_dir.IsParent(plugin)) {
50 return true;
54 return false;
57 } // namespace
59 PluginPrefs::PluginState::PluginState() {
62 PluginPrefs::PluginState::~PluginState() {
65 bool PluginPrefs::PluginState::Get(const base::FilePath& plugin,
66 bool* enabled) const {
67 base::FilePath key = ConvertMapKey(plugin);
68 std::map<base::FilePath, bool>::const_iterator iter = state_.find(key);
69 if (iter != state_.end()) {
70 *enabled = iter->second;
71 return true;
73 return false;
76 void PluginPrefs::PluginState::Set(const base::FilePath& plugin, bool enabled) {
77 state_[ConvertMapKey(plugin)] = enabled;
80 base::FilePath PluginPrefs::PluginState::ConvertMapKey(
81 const base::FilePath& plugin) const {
82 // Keep the state of component-updated and bundled Pepper Flash in sync.
83 if (IsComponentUpdatedPepperFlash(plugin)) {
84 base::FilePath bundled_pepper_flash;
85 if (PathService::Get(chrome::FILE_PEPPER_FLASH_PLUGIN,
86 &bundled_pepper_flash)) {
87 return bundled_pepper_flash;
91 return plugin;
94 // static
95 scoped_refptr<PluginPrefs> PluginPrefs::GetForProfile(Profile* profile) {
96 return PluginPrefsFactory::GetPrefsForProfile(profile);
99 // static
100 scoped_refptr<PluginPrefs> PluginPrefs::GetForTestingProfile(
101 Profile* profile) {
102 return static_cast<PluginPrefs*>(
103 PluginPrefsFactory::GetInstance()->SetTestingFactoryAndUse(
104 profile, &PluginPrefsFactory::CreateForTestingProfile).get());
107 void PluginPrefs::EnablePluginGroup(bool enabled,
108 const base::string16& group_name) {
109 PluginService::GetInstance()->GetPlugins(
110 base::Bind(&PluginPrefs::EnablePluginGroupInternal,
111 this, enabled, group_name));
114 void PluginPrefs::EnablePluginGroupInternal(
115 bool enabled,
116 const base::string16& group_name,
117 const std::vector<content::WebPluginInfo>& plugins) {
118 base::AutoLock auto_lock(lock_);
119 PluginFinder* finder = PluginFinder::GetInstance();
121 // Set the desired state for the group.
122 plugin_group_state_[group_name] = enabled;
124 // Update the state for all plugins in the group.
125 for (size_t i = 0; i < plugins.size(); ++i) {
126 scoped_ptr<PluginMetadata> plugin(finder->GetPluginMetadata(plugins[i]));
127 if (group_name != plugin->name())
128 continue;
129 plugin_state_.Set(plugins[i].path, enabled);
132 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
133 base::Bind(&PluginPrefs::OnUpdatePreferences, this, plugins));
134 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
135 base::Bind(&PluginPrefs::NotifyPluginStatusChanged, this));
138 void PluginPrefs::EnablePlugin(
139 bool enabled, const base::FilePath& path,
140 const base::Callback<void(bool)>& callback) {
141 PluginFinder* finder = PluginFinder::GetInstance();
142 content::WebPluginInfo plugin;
143 bool can_enable = true;
144 if (PluginService::GetInstance()->GetPluginInfoByPath(path, &plugin)) {
145 scoped_ptr<PluginMetadata> plugin_metadata(
146 finder->GetPluginMetadata(plugin));
147 PolicyStatus plugin_status = PolicyStatusForPlugin(plugin.name);
148 PolicyStatus group_status = PolicyStatusForPlugin(plugin_metadata->name());
149 if (enabled) {
150 if (plugin_status == POLICY_DISABLED || group_status == POLICY_DISABLED)
151 can_enable = false;
152 } else {
153 if (plugin_status == POLICY_ENABLED || group_status == POLICY_ENABLED)
154 can_enable = false;
156 } else {
157 NOTREACHED();
160 if (!can_enable) {
161 base::MessageLoop::current()->PostTask(FROM_HERE,
162 base::Bind(callback, false));
163 return;
166 PluginService::GetInstance()->GetPlugins(
167 base::Bind(&PluginPrefs::EnablePluginInternal, this,
168 enabled, path, finder, callback));
171 void PluginPrefs::EnablePluginInternal(
172 bool enabled,
173 const base::FilePath& path,
174 PluginFinder* plugin_finder,
175 const base::Callback<void(bool)>& callback,
176 const std::vector<content::WebPluginInfo>& plugins) {
178 // Set the desired state for the plugin.
179 base::AutoLock auto_lock(lock_);
180 plugin_state_.Set(path, enabled);
183 base::string16 group_name;
184 for (size_t i = 0; i < plugins.size(); ++i) {
185 if (plugins[i].path == path) {
186 scoped_ptr<PluginMetadata> plugin_metadata(
187 plugin_finder->GetPluginMetadata(plugins[i]));
188 // set the group name for this plugin.
189 group_name = plugin_metadata->name();
190 DCHECK_EQ(enabled, IsPluginEnabled(plugins[i]));
191 break;
195 bool all_disabled = true;
196 for (size_t i = 0; i < plugins.size(); ++i) {
197 scoped_ptr<PluginMetadata> plugin_metadata(
198 plugin_finder->GetPluginMetadata(plugins[i]));
199 DCHECK(!plugin_metadata->name().empty());
200 if (group_name == plugin_metadata->name()) {
201 all_disabled = all_disabled && !IsPluginEnabled(plugins[i]);
205 if (!group_name.empty()) {
206 // Update the state for the corresponding plugin group.
207 base::AutoLock auto_lock(lock_);
208 plugin_group_state_[group_name] = !all_disabled;
211 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
212 base::Bind(&PluginPrefs::OnUpdatePreferences, this, plugins));
213 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
214 base::Bind(&PluginPrefs::NotifyPluginStatusChanged, this));
215 callback.Run(true);
218 PluginPrefs::PolicyStatus PluginPrefs::PolicyStatusForPlugin(
219 const base::string16& name) const {
220 base::AutoLock auto_lock(lock_);
221 if (IsStringMatchedInSet(name, policy_enabled_plugin_patterns_)) {
222 return POLICY_ENABLED;
223 } else if (IsStringMatchedInSet(name, policy_disabled_plugin_patterns_) &&
224 !IsStringMatchedInSet(
225 name, policy_disabled_plugin_exception_patterns_)) {
226 return POLICY_DISABLED;
227 } else {
228 return NO_POLICY;
232 bool PluginPrefs::IsPluginEnabled(const content::WebPluginInfo& plugin) const {
233 scoped_ptr<PluginMetadata> plugin_metadata(
234 PluginFinder::GetInstance()->GetPluginMetadata(plugin));
235 base::string16 group_name = plugin_metadata->name();
237 // Check if the plugin or its group is enabled by policy.
238 PolicyStatus plugin_status = PolicyStatusForPlugin(plugin.name);
239 PolicyStatus group_status = PolicyStatusForPlugin(group_name);
240 if (plugin_status == POLICY_ENABLED || group_status == POLICY_ENABLED)
241 return true;
243 // Check if the plugin or its group is disabled by policy.
244 if (plugin_status == POLICY_DISABLED || group_status == POLICY_DISABLED)
245 return false;
247 #if !defined(DISABLE_NACL)
248 // If enabling NaCl, make sure the plugin is also enabled. See bug
249 // http://code.google.com/p/chromium/issues/detail?id=81010 for more
250 // information.
251 // TODO(dspringer): When NaCl is on by default, remove this code.
252 if ((plugin.name == base::ASCIIToUTF16(nacl::kNaClPluginName)) &&
253 base::CommandLine::ForCurrentProcess()->HasSwitch(
254 switches::kEnableNaCl)) {
255 return true;
257 #endif
259 base::AutoLock auto_lock(lock_);
260 // Check user preferences for the plugin.
261 bool plugin_enabled = false;
262 if (plugin_state_.Get(plugin.path, &plugin_enabled))
263 return plugin_enabled;
265 // Check user preferences for the plugin group.
266 std::map<base::string16, bool>::const_iterator group_it(
267 plugin_group_state_.find(group_name));
268 if (group_it != plugin_group_state_.end())
269 return group_it->second;
271 // Default to enabled.
272 return true;
275 void PluginPrefs::UpdatePatternsAndNotify(std::set<base::string16>* patterns,
276 const std::string& pref_name) {
277 base::AutoLock auto_lock(lock_);
278 ListValueToStringSet(prefs_->GetList(pref_name.c_str()), patterns);
280 NotifyPluginStatusChanged();
283 /*static*/
284 bool PluginPrefs::IsStringMatchedInSet(
285 const base::string16& name,
286 const std::set<base::string16>& pattern_set) {
287 std::set<base::string16>::const_iterator pattern(pattern_set.begin());
288 while (pattern != pattern_set.end()) {
289 if (MatchPattern(name, *pattern))
290 return true;
291 ++pattern;
294 return false;
297 /* static */
298 void PluginPrefs::ListValueToStringSet(const base::ListValue* src,
299 std::set<base::string16>* dest) {
300 DCHECK(src);
301 DCHECK(dest);
302 dest->clear();
303 base::ListValue::const_iterator end(src->end());
304 for (base::ListValue::const_iterator current(src->begin());
305 current != end; ++current) {
306 base::string16 plugin_name;
307 if ((*current)->GetAsString(&plugin_name)) {
308 dest->insert(plugin_name);
313 void PluginPrefs::SetPrefs(PrefService* prefs) {
314 prefs_ = prefs;
315 bool update_internal_dir = false;
316 base::FilePath last_internal_dir =
317 prefs_->GetFilePath(prefs::kPluginsLastInternalDirectory);
318 base::FilePath cur_internal_dir;
319 if (PathService::Get(chrome::DIR_INTERNAL_PLUGINS, &cur_internal_dir) &&
320 cur_internal_dir != last_internal_dir) {
321 update_internal_dir = true;
322 prefs_->SetFilePath(
323 prefs::kPluginsLastInternalDirectory, cur_internal_dir);
326 bool migrate_to_pepper_flash = false;
327 #if defined(OS_WIN) || defined(OS_MACOSX)
328 // If bundled NPAPI Flash is enabled while Pepper Flash is disabled, we
329 // would like to turn Pepper Flash on. And we only want to do it once.
330 // TODO(yzshen): Remove all |migrate_to_pepper_flash|-related code after it
331 // has been run once by most users. (Maybe Chrome 24 or Chrome 25.)
332 // NOTE(shess): Keep in mind that Mac is on a different schedule.
333 if (!prefs_->GetBoolean(prefs::kPluginsMigratedToPepperFlash)) {
334 prefs_->SetBoolean(prefs::kPluginsMigratedToPepperFlash, true);
335 migrate_to_pepper_flash = true;
337 #endif
339 bool remove_component_pepper_flash_settings = false;
340 // If component-updated Pepper Flash is disabled, we would like to remove that
341 // settings item. And we only want to do it once. (Please see the comments of
342 // kPluginsRemovedOldComponentPepperFlashSettings for why.)
343 // TODO(yzshen): Remove all |remove_component_pepper_flash_settings|-related
344 // code after it has been run once by most users.
345 if (!prefs_->GetBoolean(
346 prefs::kPluginsRemovedOldComponentPepperFlashSettings)) {
347 prefs_->SetBoolean(prefs::kPluginsRemovedOldComponentPepperFlashSettings,
348 true);
349 remove_component_pepper_flash_settings = true;
352 { // Scoped update of prefs::kPluginsPluginsList.
353 ListPrefUpdate update(prefs_, prefs::kPluginsPluginsList);
354 base::ListValue* saved_plugins_list = update.Get();
355 if (saved_plugins_list && !saved_plugins_list->empty()) {
356 // The following four variables are only valid when
357 // |migrate_to_pepper_flash| is set to true.
358 base::FilePath npapi_flash;
359 base::FilePath pepper_flash;
360 base::DictionaryValue* pepper_flash_node = NULL;
361 bool npapi_flash_enabled = false;
362 if (migrate_to_pepper_flash) {
363 PathService::Get(chrome::FILE_FLASH_PLUGIN, &npapi_flash);
364 PathService::Get(chrome::FILE_PEPPER_FLASH_PLUGIN, &pepper_flash);
367 // Used when |remove_component_pepper_flash_settings| is set to true.
368 base::ListValue::iterator component_pepper_flash_node =
369 saved_plugins_list->end();
371 for (base::ListValue::iterator it = saved_plugins_list->begin();
372 it != saved_plugins_list->end();
373 ++it) {
374 if (!(*it)->IsType(base::Value::TYPE_DICTIONARY)) {
375 LOG(WARNING) << "Invalid entry in " << prefs::kPluginsPluginsList;
376 continue; // Oops, don't know what to do with this item.
379 base::DictionaryValue* plugin =
380 static_cast<base::DictionaryValue*>(*it);
381 base::string16 group_name;
382 bool enabled;
383 if (!plugin->GetBoolean("enabled", &enabled))
384 enabled = true;
386 base::FilePath::StringType path;
387 // The plugin list constains all the plugin files in addition to the
388 // plugin groups.
389 if (plugin->GetString("path", &path)) {
390 // Files have a path attribute, groups don't.
391 base::FilePath plugin_path(path);
393 // The path to the intenral plugin directory changes everytime Chrome
394 // is auto-updated, since it contains the current version number. For
395 // example, it changes from foobar\Chrome\Application\21.0.1180.83 to
396 // foobar\Chrome\Application\21.0.1180.89.
397 // However, we would like the settings of internal plugins to persist
398 // across Chrome updates. Therefore, we need to recognize those paths
399 // that are within the previous internal plugin directory, and update
400 // them in the prefs accordingly.
401 if (update_internal_dir) {
402 base::FilePath relative_path;
404 // Extract the part of |plugin_path| that is relative to
405 // |last_internal_dir|. For example, |relative_path| will be
406 // foo\bar.dll if |plugin_path| is <last_internal_dir>\foo\bar.dll.
408 // Every iteration the last path component from |plugin_path| is
409 // removed and prepended to |relative_path| until we get up to
410 // |last_internal_dir|.
411 while (last_internal_dir.IsParent(plugin_path)) {
412 relative_path = plugin_path.BaseName().Append(relative_path);
414 base::FilePath old_path = plugin_path;
415 plugin_path = plugin_path.DirName();
416 // To be extra sure that we won't end up in an infinite loop.
417 if (old_path == plugin_path) {
418 NOTREACHED();
419 break;
423 // If |relative_path| is empty, |plugin_path| is not within
424 // |last_internal_dir|. We don't need to update it.
425 if (!relative_path.empty()) {
426 plugin_path = cur_internal_dir.Append(relative_path);
427 path = plugin_path.value();
428 plugin->SetString("path", path);
432 if (migrate_to_pepper_flash &&
433 base::FilePath::CompareEqualIgnoreCase(
434 path, npapi_flash.value())) {
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;
441 } else if (remove_component_pepper_flash_settings &&
442 IsComponentUpdatedPepperFlash(plugin_path)) {
443 if (!enabled) {
444 component_pepper_flash_node = it;
445 // Skip setting |enabled| into |plugin_state_|.
446 continue;
450 plugin_state_.Set(plugin_path, enabled);
451 } else if (!enabled && plugin->GetString("name", &group_name)) {
452 // Otherwise this is a list of groups.
453 plugin_group_state_[group_name] = false;
457 if (npapi_flash_enabled && pepper_flash_node) {
458 DCHECK(migrate_to_pepper_flash);
459 pepper_flash_node->SetBoolean("enabled", true);
460 plugin_state_.Set(pepper_flash, true);
463 if (component_pepper_flash_node != saved_plugins_list->end()) {
464 DCHECK(remove_component_pepper_flash_settings);
465 saved_plugins_list->Erase(component_pepper_flash_node, NULL);
467 } else {
468 // If the saved plugin list is empty, then the call to UpdatePreferences()
469 // below failed in an earlier run, possibly because the user closed the
470 // browser too quickly.
472 // Only want one PDF plugin enabled at a time. See http://crbug.com/50105
473 // for background.
474 plugin_group_state_[base::ASCIIToUTF16(
475 PluginMetadata::kAdobeReaderGroupName)] = false;
477 } // Scoped update of prefs::kPluginsPluginsList.
479 // Build the set of policy enabled/disabled plugin patterns once and cache it.
480 // Don't do this in the constructor, there's no profile available there.
481 ListValueToStringSet(prefs_->GetList(prefs::kPluginsDisabledPlugins),
482 &policy_disabled_plugin_patterns_);
483 ListValueToStringSet(
484 prefs_->GetList(prefs::kPluginsDisabledPluginsExceptions),
485 &policy_disabled_plugin_exception_patterns_);
486 ListValueToStringSet(prefs_->GetList(prefs::kPluginsEnabledPlugins),
487 &policy_enabled_plugin_patterns_);
489 registrar_.Init(prefs_);
491 // Because pointers to our own members will remain unchanged for the
492 // lifetime of |registrar_| (which we also own), we can bind their
493 // pointer values directly in the callbacks to avoid string-based
494 // lookups at notification time.
495 registrar_.Add(prefs::kPluginsDisabledPlugins,
496 base::Bind(&PluginPrefs::UpdatePatternsAndNotify,
497 base::Unretained(this),
498 &policy_disabled_plugin_patterns_));
499 registrar_.Add(prefs::kPluginsDisabledPluginsExceptions,
500 base::Bind(&PluginPrefs::UpdatePatternsAndNotify,
501 base::Unretained(this),
502 &policy_disabled_plugin_exception_patterns_));
503 registrar_.Add(prefs::kPluginsEnabledPlugins,
504 base::Bind(&PluginPrefs::UpdatePatternsAndNotify,
505 base::Unretained(this),
506 &policy_enabled_plugin_patterns_));
508 NotifyPluginStatusChanged();
511 void PluginPrefs::ShutdownOnUIThread() {
512 prefs_ = NULL;
513 registrar_.RemoveAll();
516 PluginPrefs::PluginPrefs() : profile_(NULL),
517 prefs_(NULL) {
520 PluginPrefs::~PluginPrefs() {
523 void PluginPrefs::SetPolicyEnforcedPluginPatterns(
524 const std::set<base::string16>& disabled_patterns,
525 const std::set<base::string16>& disabled_exception_patterns,
526 const std::set<base::string16>& enabled_patterns) {
527 policy_disabled_plugin_patterns_ = disabled_patterns;
528 policy_disabled_plugin_exception_patterns_ = disabled_exception_patterns;
529 policy_enabled_plugin_patterns_ = enabled_patterns;
532 void PluginPrefs::OnUpdatePreferences(
533 const std::vector<content::WebPluginInfo>& plugins) {
534 if (!prefs_)
535 return;
537 PluginFinder* finder = PluginFinder::GetInstance();
538 ListPrefUpdate update(prefs_, prefs::kPluginsPluginsList);
539 base::ListValue* plugins_list = update.Get();
540 plugins_list->Clear();
542 base::FilePath internal_dir;
543 if (PathService::Get(chrome::DIR_INTERNAL_PLUGINS, &internal_dir))
544 prefs_->SetFilePath(prefs::kPluginsLastInternalDirectory, internal_dir);
546 base::AutoLock auto_lock(lock_);
548 // Add the plugin files.
549 std::set<base::string16> group_names;
550 for (size_t i = 0; i < plugins.size(); ++i) {
551 base::DictionaryValue* summary = new base::DictionaryValue();
552 summary->SetString("path", plugins[i].path.value());
553 summary->SetString("name", plugins[i].name);
554 summary->SetString("version", plugins[i].version);
555 bool enabled = true;
556 plugin_state_.Get(plugins[i].path, &enabled);
557 summary->SetBoolean("enabled", enabled);
558 plugins_list->Append(summary);
560 scoped_ptr<PluginMetadata> plugin_metadata(
561 finder->GetPluginMetadata(plugins[i]));
562 // Insert into a set of all group names.
563 group_names.insert(plugin_metadata->name());
566 // Add the plugin groups.
567 for (std::set<base::string16>::const_iterator it = group_names.begin();
568 it != group_names.end(); ++it) {
569 base::DictionaryValue* summary = new base::DictionaryValue();
570 summary->SetString("name", *it);
571 bool enabled = true;
572 std::map<base::string16, bool>::iterator gstate_it =
573 plugin_group_state_.find(*it);
574 if (gstate_it != plugin_group_state_.end())
575 enabled = gstate_it->second;
576 summary->SetBoolean("enabled", enabled);
577 plugins_list->Append(summary);
581 void PluginPrefs::NotifyPluginStatusChanged() {
582 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
583 content::NotificationService::current()->Notify(
584 chrome::NOTIFICATION_PLUGIN_ENABLE_STATUS_CHANGED,
585 content::Source<Profile>(profile_),
586 content::NotificationService::NoDetails());