[Metrics] Make MetricsStateManager take a callback param to check if UMA is enabled.
[chromium-blink-merge.git] / chrome / browser / plugins / plugin_prefs.cc
blobd8cc025c0702adc3a274442e12748aa213442875
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_content_client.h"
27 #include "chrome/common/chrome_paths.h"
28 #include "chrome/common/chrome_switches.h"
29 #include "chrome/common/pref_names.h"
30 #include "components/keyed_service/core/keyed_service.h"
31 #include "content/public/browser/browser_thread.h"
32 #include "content/public/browser/notification_service.h"
33 #include "content/public/browser/plugin_service.h"
34 #include "content/public/common/webplugininfo.h"
36 using content::BrowserThread;
37 using content::PluginService;
39 namespace {
41 bool IsComponentUpdatedPepperFlash(const base::FilePath& plugin) {
42 if (plugin.BaseName().value() == chrome::kPepperFlashPluginFilename) {
43 base::FilePath component_updated_pepper_flash_dir;
44 if (PathService::Get(chrome::DIR_COMPONENT_UPDATED_PEPPER_FLASH_PLUGIN,
45 &component_updated_pepper_flash_dir) &&
46 component_updated_pepper_flash_dir.IsParent(plugin)) {
47 return true;
51 return false;
54 } // namespace
56 PluginPrefs::PluginState::PluginState() {
59 PluginPrefs::PluginState::~PluginState() {
62 bool PluginPrefs::PluginState::Get(const base::FilePath& plugin,
63 bool* enabled) const {
64 base::FilePath key = ConvertMapKey(plugin);
65 std::map<base::FilePath, bool>::const_iterator iter = state_.find(key);
66 if (iter != state_.end()) {
67 *enabled = iter->second;
68 return true;
70 return false;
73 void PluginPrefs::PluginState::Set(const base::FilePath& plugin, bool enabled) {
74 state_[ConvertMapKey(plugin)] = enabled;
77 base::FilePath PluginPrefs::PluginState::ConvertMapKey(
78 const base::FilePath& plugin) const {
79 // Keep the state of component-updated and bundled Pepper Flash in sync.
80 if (IsComponentUpdatedPepperFlash(plugin)) {
81 base::FilePath bundled_pepper_flash;
82 if (PathService::Get(chrome::FILE_PEPPER_FLASH_PLUGIN,
83 &bundled_pepper_flash)) {
84 return bundled_pepper_flash;
88 return plugin;
91 // static
92 scoped_refptr<PluginPrefs> PluginPrefs::GetForProfile(Profile* profile) {
93 return PluginPrefsFactory::GetPrefsForProfile(profile);
96 // static
97 scoped_refptr<PluginPrefs> PluginPrefs::GetForTestingProfile(
98 Profile* profile) {
99 return static_cast<PluginPrefs*>(
100 PluginPrefsFactory::GetInstance()->SetTestingFactoryAndUse(
101 profile, &PluginPrefsFactory::CreateForTestingProfile).get());
104 void PluginPrefs::EnablePluginGroup(bool enabled,
105 const base::string16& group_name) {
106 PluginService::GetInstance()->GetPlugins(
107 base::Bind(&PluginPrefs::EnablePluginGroupInternal,
108 this, enabled, group_name));
111 void PluginPrefs::EnablePluginGroupInternal(
112 bool enabled,
113 const base::string16& group_name,
114 const std::vector<content::WebPluginInfo>& plugins) {
115 base::AutoLock auto_lock(lock_);
116 PluginFinder* finder = PluginFinder::GetInstance();
118 // Set the desired state for the group.
119 plugin_group_state_[group_name] = enabled;
121 // Update the state for all plug-ins in the group.
122 for (size_t i = 0; i < plugins.size(); ++i) {
123 scoped_ptr<PluginMetadata> plugin(finder->GetPluginMetadata(plugins[i]));
124 if (group_name != plugin->name())
125 continue;
126 plugin_state_.Set(plugins[i].path, enabled);
129 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
130 base::Bind(&PluginPrefs::OnUpdatePreferences, this, plugins));
131 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
132 base::Bind(&PluginPrefs::NotifyPluginStatusChanged, this));
135 void PluginPrefs::EnablePlugin(
136 bool enabled, const base::FilePath& path,
137 const base::Callback<void(bool)>& callback) {
138 PluginFinder* finder = PluginFinder::GetInstance();
139 content::WebPluginInfo plugin;
140 bool can_enable = true;
141 if (PluginService::GetInstance()->GetPluginInfoByPath(path, &plugin)) {
142 scoped_ptr<PluginMetadata> plugin_metadata(
143 finder->GetPluginMetadata(plugin));
144 PolicyStatus plugin_status = PolicyStatusForPlugin(plugin.name);
145 PolicyStatus group_status = PolicyStatusForPlugin(plugin_metadata->name());
146 if (enabled) {
147 if (plugin_status == POLICY_DISABLED || group_status == POLICY_DISABLED)
148 can_enable = false;
149 } else {
150 if (plugin_status == POLICY_ENABLED || group_status == POLICY_ENABLED)
151 can_enable = false;
153 } else {
154 NOTREACHED();
157 if (!can_enable) {
158 base::MessageLoop::current()->PostTask(FROM_HERE,
159 base::Bind(callback, false));
160 return;
163 PluginService::GetInstance()->GetPlugins(
164 base::Bind(&PluginPrefs::EnablePluginInternal, this,
165 enabled, path, finder, callback));
168 void PluginPrefs::EnablePluginInternal(
169 bool enabled,
170 const base::FilePath& path,
171 PluginFinder* plugin_finder,
172 const base::Callback<void(bool)>& callback,
173 const std::vector<content::WebPluginInfo>& plugins) {
175 // Set the desired state for the plug-in.
176 base::AutoLock auto_lock(lock_);
177 plugin_state_.Set(path, enabled);
180 base::string16 group_name;
181 for (size_t i = 0; i < plugins.size(); ++i) {
182 if (plugins[i].path == path) {
183 scoped_ptr<PluginMetadata> plugin_metadata(
184 plugin_finder->GetPluginMetadata(plugins[i]));
185 // set the group name for this plug-in.
186 group_name = plugin_metadata->name();
187 DCHECK_EQ(enabled, IsPluginEnabled(plugins[i]));
188 break;
192 bool all_disabled = true;
193 for (size_t i = 0; i < plugins.size(); ++i) {
194 scoped_ptr<PluginMetadata> plugin_metadata(
195 plugin_finder->GetPluginMetadata(plugins[i]));
196 DCHECK(!plugin_metadata->name().empty());
197 if (group_name == plugin_metadata->name()) {
198 all_disabled = all_disabled && !IsPluginEnabled(plugins[i]);
202 if (!group_name.empty()) {
203 // Update the state for the corresponding plug-in group.
204 base::AutoLock auto_lock(lock_);
205 plugin_group_state_[group_name] = !all_disabled;
208 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
209 base::Bind(&PluginPrefs::OnUpdatePreferences, this, plugins));
210 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
211 base::Bind(&PluginPrefs::NotifyPluginStatusChanged, this));
212 callback.Run(true);
215 PluginPrefs::PolicyStatus PluginPrefs::PolicyStatusForPlugin(
216 const base::string16& name) const {
217 base::AutoLock auto_lock(lock_);
218 if (IsStringMatchedInSet(name, policy_enabled_plugin_patterns_)) {
219 return POLICY_ENABLED;
220 } else if (IsStringMatchedInSet(name, policy_disabled_plugin_patterns_) &&
221 !IsStringMatchedInSet(
222 name, policy_disabled_plugin_exception_patterns_)) {
223 return POLICY_DISABLED;
224 } else {
225 return NO_POLICY;
229 bool PluginPrefs::IsPluginEnabled(const content::WebPluginInfo& plugin) const {
230 scoped_ptr<PluginMetadata> plugin_metadata(
231 PluginFinder::GetInstance()->GetPluginMetadata(plugin));
232 base::string16 group_name = plugin_metadata->name();
234 // Check if the plug-in or its group is enabled by policy.
235 PolicyStatus plugin_status = PolicyStatusForPlugin(plugin.name);
236 PolicyStatus group_status = PolicyStatusForPlugin(group_name);
237 if (plugin_status == POLICY_ENABLED || group_status == POLICY_ENABLED)
238 return true;
240 // Check if the plug-in or its group is disabled by policy.
241 if (plugin_status == POLICY_DISABLED || group_status == POLICY_DISABLED)
242 return false;
244 // If enabling NaCl, make sure the plugin is also enabled. See bug
245 // http://code.google.com/p/chromium/issues/detail?id=81010 for more
246 // information.
247 // TODO(dspringer): When NaCl is on by default, remove this code.
248 if ((plugin.name ==
249 base::ASCIIToUTF16(ChromeContentClient::kNaClPluginName)) &&
250 CommandLine::ForCurrentProcess()->HasSwitch(switches::kEnableNaCl)) {
251 return true;
254 base::AutoLock auto_lock(lock_);
255 // Check user preferences for the plug-in.
256 bool plugin_enabled = false;
257 if (plugin_state_.Get(plugin.path, &plugin_enabled))
258 return plugin_enabled;
260 // Check user preferences for the plug-in group.
261 std::map<base::string16, bool>::const_iterator group_it(
262 plugin_group_state_.find(group_name));
263 if (group_it != plugin_group_state_.end())
264 return group_it->second;
266 // Default to enabled.
267 return true;
270 void PluginPrefs::UpdatePatternsAndNotify(std::set<base::string16>* patterns,
271 const std::string& pref_name) {
272 base::AutoLock auto_lock(lock_);
273 ListValueToStringSet(prefs_->GetList(pref_name.c_str()), patterns);
275 NotifyPluginStatusChanged();
278 /*static*/
279 bool PluginPrefs::IsStringMatchedInSet(
280 const base::string16& name,
281 const std::set<base::string16>& pattern_set) {
282 std::set<base::string16>::const_iterator pattern(pattern_set.begin());
283 while (pattern != pattern_set.end()) {
284 if (MatchPattern(name, *pattern))
285 return true;
286 ++pattern;
289 return false;
292 /* static */
293 void PluginPrefs::ListValueToStringSet(const base::ListValue* src,
294 std::set<base::string16>* dest) {
295 DCHECK(src);
296 DCHECK(dest);
297 dest->clear();
298 base::ListValue::const_iterator end(src->end());
299 for (base::ListValue::const_iterator current(src->begin());
300 current != end; ++current) {
301 base::string16 plugin_name;
302 if ((*current)->GetAsString(&plugin_name)) {
303 dest->insert(plugin_name);
308 void PluginPrefs::SetPrefs(PrefService* prefs) {
309 prefs_ = prefs;
310 bool update_internal_dir = false;
311 base::FilePath last_internal_dir =
312 prefs_->GetFilePath(prefs::kPluginsLastInternalDirectory);
313 base::FilePath cur_internal_dir;
314 if (PathService::Get(chrome::DIR_INTERNAL_PLUGINS, &cur_internal_dir) &&
315 cur_internal_dir != last_internal_dir) {
316 update_internal_dir = true;
317 prefs_->SetFilePath(
318 prefs::kPluginsLastInternalDirectory, cur_internal_dir);
321 bool migrate_to_pepper_flash = false;
322 #if defined(OS_WIN) || defined(OS_MACOSX)
323 // If bundled NPAPI Flash is enabled while Pepper Flash is disabled, we
324 // would like to turn Pepper Flash on. And we only want to do it once.
325 // TODO(yzshen): Remove all |migrate_to_pepper_flash|-related code after it
326 // has been run once by most users. (Maybe Chrome 24 or Chrome 25.)
327 // NOTE(shess): Keep in mind that Mac is on a different schedule.
328 if (!prefs_->GetBoolean(prefs::kPluginsMigratedToPepperFlash)) {
329 prefs_->SetBoolean(prefs::kPluginsMigratedToPepperFlash, true);
330 migrate_to_pepper_flash = true;
332 #endif
334 bool remove_component_pepper_flash_settings = false;
335 // If component-updated Pepper Flash is disabled, we would like to remove that
336 // settings item. And we only want to do it once. (Please see the comments of
337 // kPluginsRemovedOldComponentPepperFlashSettings for why.)
338 // TODO(yzshen): Remove all |remove_component_pepper_flash_settings|-related
339 // code after it has been run once by most users.
340 if (!prefs_->GetBoolean(
341 prefs::kPluginsRemovedOldComponentPepperFlashSettings)) {
342 prefs_->SetBoolean(prefs::kPluginsRemovedOldComponentPepperFlashSettings,
343 true);
344 remove_component_pepper_flash_settings = true;
347 { // Scoped update of prefs::kPluginsPluginsList.
348 ListPrefUpdate update(prefs_, prefs::kPluginsPluginsList);
349 base::ListValue* saved_plugins_list = update.Get();
350 if (saved_plugins_list && !saved_plugins_list->empty()) {
351 // The following four variables are only valid when
352 // |migrate_to_pepper_flash| is set to true.
353 base::FilePath npapi_flash;
354 base::FilePath pepper_flash;
355 base::DictionaryValue* pepper_flash_node = NULL;
356 bool npapi_flash_enabled = false;
357 if (migrate_to_pepper_flash) {
358 PathService::Get(chrome::FILE_FLASH_PLUGIN, &npapi_flash);
359 PathService::Get(chrome::FILE_PEPPER_FLASH_PLUGIN, &pepper_flash);
362 // Used when |remove_component_pepper_flash_settings| is set to true.
363 base::ListValue::iterator component_pepper_flash_node =
364 saved_plugins_list->end();
366 for (base::ListValue::iterator it = saved_plugins_list->begin();
367 it != saved_plugins_list->end();
368 ++it) {
369 if (!(*it)->IsType(base::Value::TYPE_DICTIONARY)) {
370 LOG(WARNING) << "Invalid entry in " << prefs::kPluginsPluginsList;
371 continue; // Oops, don't know what to do with this item.
374 base::DictionaryValue* plugin =
375 static_cast<base::DictionaryValue*>(*it);
376 base::string16 group_name;
377 bool enabled;
378 if (!plugin->GetBoolean("enabled", &enabled))
379 enabled = true;
381 base::FilePath::StringType path;
382 // The plugin list constains all the plugin files in addition to the
383 // plugin groups.
384 if (plugin->GetString("path", &path)) {
385 // Files have a path attribute, groups don't.
386 base::FilePath plugin_path(path);
388 // The path to the intenral plugin directory changes everytime Chrome
389 // is auto-updated, since it contains the current version number. For
390 // example, it changes from foobar\Chrome\Application\21.0.1180.83 to
391 // foobar\Chrome\Application\21.0.1180.89.
392 // However, we would like the settings of internal plugins to persist
393 // across Chrome updates. Therefore, we need to recognize those paths
394 // that are within the previous internal plugin directory, and update
395 // them in the prefs accordingly.
396 if (update_internal_dir) {
397 base::FilePath relative_path;
399 // Extract the part of |plugin_path| that is relative to
400 // |last_internal_dir|. For example, |relative_path| will be
401 // foo\bar.dll if |plugin_path| is <last_internal_dir>\foo\bar.dll.
403 // Every iteration the last path component from |plugin_path| is
404 // removed and prepended to |relative_path| until we get up to
405 // |last_internal_dir|.
406 while (last_internal_dir.IsParent(plugin_path)) {
407 relative_path = plugin_path.BaseName().Append(relative_path);
409 base::FilePath old_path = plugin_path;
410 plugin_path = plugin_path.DirName();
411 // To be extra sure that we won't end up in an infinite loop.
412 if (old_path == plugin_path) {
413 NOTREACHED();
414 break;
418 // If |relative_path| is empty, |plugin_path| is not within
419 // |last_internal_dir|. We don't need to update it.
420 if (!relative_path.empty()) {
421 plugin_path = cur_internal_dir.Append(relative_path);
422 path = plugin_path.value();
423 plugin->SetString("path", path);
427 if (migrate_to_pepper_flash &&
428 base::FilePath::CompareEqualIgnoreCase(
429 path, npapi_flash.value())) {
430 npapi_flash_enabled = enabled;
431 } else if (migrate_to_pepper_flash &&
432 base::FilePath::CompareEqualIgnoreCase(
433 path, pepper_flash.value())) {
434 if (!enabled)
435 pepper_flash_node = plugin;
436 } else if (remove_component_pepper_flash_settings &&
437 IsComponentUpdatedPepperFlash(plugin_path)) {
438 if (!enabled) {
439 component_pepper_flash_node = it;
440 // Skip setting |enabled| into |plugin_state_|.
441 continue;
445 plugin_state_.Set(plugin_path, enabled);
446 } else if (!enabled && plugin->GetString("name", &group_name)) {
447 // Otherwise this is a list of groups.
448 plugin_group_state_[group_name] = false;
452 if (npapi_flash_enabled && pepper_flash_node) {
453 DCHECK(migrate_to_pepper_flash);
454 pepper_flash_node->SetBoolean("enabled", true);
455 plugin_state_.Set(pepper_flash, true);
458 if (component_pepper_flash_node != saved_plugins_list->end()) {
459 DCHECK(remove_component_pepper_flash_settings);
460 saved_plugins_list->Erase(component_pepper_flash_node, NULL);
462 } else {
463 // If the saved plugin list is empty, then the call to UpdatePreferences()
464 // below failed in an earlier run, possibly because the user closed the
465 // browser too quickly.
467 // Only want one PDF plugin enabled at a time. See http://crbug.com/50105
468 // for background.
469 plugin_group_state_[base::ASCIIToUTF16(
470 PluginMetadata::kAdobeReaderGroupName)] = false;
472 } // Scoped update of prefs::kPluginsPluginsList.
474 // Build the set of policy enabled/disabled plugin patterns once and cache it.
475 // Don't do this in the constructor, there's no profile available there.
476 ListValueToStringSet(prefs_->GetList(prefs::kPluginsDisabledPlugins),
477 &policy_disabled_plugin_patterns_);
478 ListValueToStringSet(
479 prefs_->GetList(prefs::kPluginsDisabledPluginsExceptions),
480 &policy_disabled_plugin_exception_patterns_);
481 ListValueToStringSet(prefs_->GetList(prefs::kPluginsEnabledPlugins),
482 &policy_enabled_plugin_patterns_);
484 registrar_.Init(prefs_);
486 // Because pointers to our own members will remain unchanged for the
487 // lifetime of |registrar_| (which we also own), we can bind their
488 // pointer values directly in the callbacks to avoid string-based
489 // lookups at notification time.
490 registrar_.Add(prefs::kPluginsDisabledPlugins,
491 base::Bind(&PluginPrefs::UpdatePatternsAndNotify,
492 base::Unretained(this),
493 &policy_disabled_plugin_patterns_));
494 registrar_.Add(prefs::kPluginsDisabledPluginsExceptions,
495 base::Bind(&PluginPrefs::UpdatePatternsAndNotify,
496 base::Unretained(this),
497 &policy_disabled_plugin_exception_patterns_));
498 registrar_.Add(prefs::kPluginsEnabledPlugins,
499 base::Bind(&PluginPrefs::UpdatePatternsAndNotify,
500 base::Unretained(this),
501 &policy_enabled_plugin_patterns_));
503 NotifyPluginStatusChanged();
506 void PluginPrefs::ShutdownOnUIThread() {
507 prefs_ = NULL;
508 registrar_.RemoveAll();
511 PluginPrefs::PluginPrefs() : profile_(NULL),
512 prefs_(NULL) {
515 PluginPrefs::~PluginPrefs() {
518 void PluginPrefs::SetPolicyEnforcedPluginPatterns(
519 const std::set<base::string16>& disabled_patterns,
520 const std::set<base::string16>& disabled_exception_patterns,
521 const std::set<base::string16>& enabled_patterns) {
522 policy_disabled_plugin_patterns_ = disabled_patterns;
523 policy_disabled_plugin_exception_patterns_ = disabled_exception_patterns;
524 policy_enabled_plugin_patterns_ = enabled_patterns;
527 void PluginPrefs::OnUpdatePreferences(
528 const std::vector<content::WebPluginInfo>& plugins) {
529 if (!prefs_)
530 return;
532 PluginFinder* finder = PluginFinder::GetInstance();
533 ListPrefUpdate update(prefs_, prefs::kPluginsPluginsList);
534 base::ListValue* plugins_list = update.Get();
535 plugins_list->Clear();
537 base::FilePath internal_dir;
538 if (PathService::Get(chrome::DIR_INTERNAL_PLUGINS, &internal_dir))
539 prefs_->SetFilePath(prefs::kPluginsLastInternalDirectory, internal_dir);
541 base::AutoLock auto_lock(lock_);
543 // Add the plugin files.
544 std::set<base::string16> group_names;
545 for (size_t i = 0; i < plugins.size(); ++i) {
546 base::DictionaryValue* summary = new base::DictionaryValue();
547 summary->SetString("path", plugins[i].path.value());
548 summary->SetString("name", plugins[i].name);
549 summary->SetString("version", plugins[i].version);
550 bool enabled = true;
551 plugin_state_.Get(plugins[i].path, &enabled);
552 summary->SetBoolean("enabled", enabled);
553 plugins_list->Append(summary);
555 scoped_ptr<PluginMetadata> plugin_metadata(
556 finder->GetPluginMetadata(plugins[i]));
557 // Insert into a set of all group names.
558 group_names.insert(plugin_metadata->name());
561 // Add the plug-in groups.
562 for (std::set<base::string16>::const_iterator it = group_names.begin();
563 it != group_names.end(); ++it) {
564 base::DictionaryValue* summary = new base::DictionaryValue();
565 summary->SetString("name", *it);
566 bool enabled = true;
567 std::map<base::string16, bool>::iterator gstate_it =
568 plugin_group_state_.find(*it);
569 if (gstate_it != plugin_group_state_.end())
570 enabled = gstate_it->second;
571 summary->SetBoolean("enabled", enabled);
572 plugins_list->Append(summary);
576 void PluginPrefs::NotifyPluginStatusChanged() {
577 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
578 content::NotificationService::current()->Notify(
579 chrome::NOTIFICATION_PLUGIN_ENABLE_STATUS_CHANGED,
580 content::Source<Profile>(profile_),
581 content::NotificationService::NoDetails());