Home | History | Annotate | Download | only in plugins
      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.
      4 
      5 #include "chrome/browser/plugins/plugin_prefs.h"
      6 
      7 #include <string>
      8 
      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"
     34 
     35 #if !defined(DISABLE_NACL)
     36 #include "components/nacl/common/nacl_constants.h"
     37 #endif
     38 
     39 using content::BrowserThread;
     40 using content::PluginService;
     41 
     42 namespace {
     43 
     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;
     51     }
     52   }
     53 
     54   return false;
     55 }
     56 
     57 }  // namespace
     58 
     59 PluginPrefs::PluginState::PluginState() {
     60 }
     61 
     62 PluginPrefs::PluginState::~PluginState() {
     63 }
     64 
     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;
     72   }
     73   return false;
     74 }
     75 
     76 void PluginPrefs::PluginState::Set(const base::FilePath& plugin, bool enabled) {
     77   state_[ConvertMapKey(plugin)] = enabled;
     78 }
     79 
     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;
     88     }
     89   }
     90 
     91   return plugin;
     92 }
     93 
     94 // static
     95 scoped_refptr<PluginPrefs> PluginPrefs::GetForProfile(Profile* profile) {
     96   return PluginPrefsFactory::GetPrefsForProfile(profile);
     97 }
     98 
     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());
    105 }
    106 
    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));
    112 }
    113 
    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();
    120 
    121   // Set the desired state for the group.
    122   plugin_group_state_[group_name] = enabled;
    123 
    124   // Update the state for all plug-ins 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);
    130   }
    131 
    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));
    136 }
    137 
    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;
    155     }
    156   } else {
    157     NOTREACHED();
    158   }
    159 
    160   if (!can_enable) {
    161     base::MessageLoop::current()->PostTask(FROM_HERE,
    162                                            base::Bind(callback, false));
    163     return;
    164   }
    165 
    166   PluginService::GetInstance()->GetPlugins(
    167       base::Bind(&PluginPrefs::EnablePluginInternal, this,
    168                  enabled, path, finder, callback));
    169 }
    170 
    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) {
    177   {
    178     // Set the desired state for the plug-in.
    179     base::AutoLock auto_lock(lock_);
    180     plugin_state_.Set(path, enabled);
    181   }
    182 
    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 plug-in.
    189       group_name = plugin_metadata->name();
    190       DCHECK_EQ(enabled, IsPluginEnabled(plugins[i]));
    191       break;
    192     }
    193   }
    194 
    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]);
    202     }
    203   }
    204 
    205   if (!group_name.empty()) {
    206     // Update the state for the corresponding plug-in group.
    207     base::AutoLock auto_lock(lock_);
    208     plugin_group_state_[group_name] = !all_disabled;
    209   }
    210 
    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);
    216 }
    217 
    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;
    229   }
    230 }
    231 
    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();
    236 
    237   // Check if the plug-in 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;
    242 
    243   // Check if the plug-in or its group is disabled by policy.
    244   if (plugin_status == POLICY_DISABLED || group_status == POLICY_DISABLED)
    245     return false;
    246 
    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       CommandLine::ForCurrentProcess()->HasSwitch(switches::kEnableNaCl)) {
    254     return true;
    255   }
    256 #endif
    257 
    258   base::AutoLock auto_lock(lock_);
    259   // Check user preferences for the plug-in.
    260   bool plugin_enabled = false;
    261   if (plugin_state_.Get(plugin.path, &plugin_enabled))
    262     return plugin_enabled;
    263 
    264   // Check user preferences for the plug-in group.
    265   std::map<base::string16, bool>::const_iterator group_it(
    266       plugin_group_state_.find(group_name));
    267   if (group_it != plugin_group_state_.end())
    268     return group_it->second;
    269 
    270   // Default to enabled.
    271   return true;
    272 }
    273 
    274 void PluginPrefs::UpdatePatternsAndNotify(std::set<base::string16>* patterns,
    275                                           const std::string& pref_name) {
    276   base::AutoLock auto_lock(lock_);
    277   ListValueToStringSet(prefs_->GetList(pref_name.c_str()), patterns);
    278 
    279   NotifyPluginStatusChanged();
    280 }
    281 
    282 /*static*/
    283 bool PluginPrefs::IsStringMatchedInSet(
    284     const base::string16& name,
    285     const std::set<base::string16>& pattern_set) {
    286   std::set<base::string16>::const_iterator pattern(pattern_set.begin());
    287   while (pattern != pattern_set.end()) {
    288     if (MatchPattern(name, *pattern))
    289       return true;
    290     ++pattern;
    291   }
    292 
    293   return false;
    294 }
    295 
    296 /* static */
    297 void PluginPrefs::ListValueToStringSet(const base::ListValue* src,
    298                                        std::set<base::string16>* dest) {
    299   DCHECK(src);
    300   DCHECK(dest);
    301   dest->clear();
    302   base::ListValue::const_iterator end(src->end());
    303   for (base::ListValue::const_iterator current(src->begin());
    304        current != end; ++current) {
    305     base::string16 plugin_name;
    306     if ((*current)->GetAsString(&plugin_name)) {
    307       dest->insert(plugin_name);
    308     }
    309   }
    310 }
    311 
    312 void PluginPrefs::SetPrefs(PrefService* prefs) {
    313   prefs_ = prefs;
    314   bool update_internal_dir = false;
    315   base::FilePath last_internal_dir =
    316       prefs_->GetFilePath(prefs::kPluginsLastInternalDirectory);
    317   base::FilePath cur_internal_dir;
    318   if (PathService::Get(chrome::DIR_INTERNAL_PLUGINS, &cur_internal_dir) &&
    319       cur_internal_dir != last_internal_dir) {
    320     update_internal_dir = true;
    321     prefs_->SetFilePath(
    322         prefs::kPluginsLastInternalDirectory, cur_internal_dir);
    323   }
    324 
    325   bool migrate_to_pepper_flash = false;
    326 #if defined(OS_WIN) || defined(OS_MACOSX)
    327   // If bundled NPAPI Flash is enabled while Pepper Flash is disabled, we
    328   // would like to turn Pepper Flash on. And we only want to do it once.
    329   // TODO(yzshen): Remove all |migrate_to_pepper_flash|-related code after it
    330   // has been run once by most users. (Maybe Chrome 24 or Chrome 25.)
    331   // NOTE(shess): Keep in mind that Mac is on a different schedule.
    332   if (!prefs_->GetBoolean(prefs::kPluginsMigratedToPepperFlash)) {
    333     prefs_->SetBoolean(prefs::kPluginsMigratedToPepperFlash, true);
    334     migrate_to_pepper_flash = true;
    335   }
    336 #endif
    337 
    338   bool remove_component_pepper_flash_settings = false;
    339   // If component-updated Pepper Flash is disabled, we would like to remove that
    340   // settings item. And we only want to do it once. (Please see the comments of
    341   // kPluginsRemovedOldComponentPepperFlashSettings for why.)
    342   // TODO(yzshen): Remove all |remove_component_pepper_flash_settings|-related
    343   // code after it has been run once by most users.
    344   if (!prefs_->GetBoolean(
    345           prefs::kPluginsRemovedOldComponentPepperFlashSettings)) {
    346     prefs_->SetBoolean(prefs::kPluginsRemovedOldComponentPepperFlashSettings,
    347                        true);
    348     remove_component_pepper_flash_settings = true;
    349   }
    350 
    351   {  // Scoped update of prefs::kPluginsPluginsList.
    352     ListPrefUpdate update(prefs_, prefs::kPluginsPluginsList);
    353     base::ListValue* saved_plugins_list = update.Get();
    354     if (saved_plugins_list && !saved_plugins_list->empty()) {
    355       // The following four variables are only valid when
    356       // |migrate_to_pepper_flash| is set to true.
    357       base::FilePath npapi_flash;
    358       base::FilePath pepper_flash;
    359       base::DictionaryValue* pepper_flash_node = NULL;
    360       bool npapi_flash_enabled = false;
    361       if (migrate_to_pepper_flash) {
    362         PathService::Get(chrome::FILE_FLASH_PLUGIN, &npapi_flash);
    363         PathService::Get(chrome::FILE_PEPPER_FLASH_PLUGIN, &pepper_flash);
    364       }
    365 
    366       // Used when |remove_component_pepper_flash_settings| is set to true.
    367       base::ListValue::iterator component_pepper_flash_node =
    368           saved_plugins_list->end();
    369 
    370       for (base::ListValue::iterator it = saved_plugins_list->begin();
    371            it != saved_plugins_list->end();
    372            ++it) {
    373         if (!(*it)->IsType(base::Value::TYPE_DICTIONARY)) {
    374           LOG(WARNING) << "Invalid entry in " << prefs::kPluginsPluginsList;
    375           continue;  // Oops, don't know what to do with this item.
    376         }
    377 
    378         base::DictionaryValue* plugin =
    379             static_cast<base::DictionaryValue*>(*it);
    380         base::string16 group_name;
    381         bool enabled;
    382         if (!plugin->GetBoolean("enabled", &enabled))
    383           enabled = true;
    384 
    385         base::FilePath::StringType path;
    386         // The plugin list constains all the plugin files in addition to the
    387         // plugin groups.
    388         if (plugin->GetString("path", &path)) {
    389           // Files have a path attribute, groups don't.
    390           base::FilePath plugin_path(path);
    391 
    392           // The path to the intenral plugin directory changes everytime Chrome
    393           // is auto-updated, since it contains the current version number. For
    394           // example, it changes from foobar\Chrome\Application\21.0.1180.83 to
    395           // foobar\Chrome\Application\21.0.1180.89.
    396           // However, we would like the settings of internal plugins to persist
    397           // across Chrome updates. Therefore, we need to recognize those paths
    398           // that are within the previous internal plugin directory, and update
    399           // them in the prefs accordingly.
    400           if (update_internal_dir) {
    401             base::FilePath relative_path;
    402 
    403             // Extract the part of |plugin_path| that is relative to
    404             // |last_internal_dir|. For example, |relative_path| will be
    405             // foo\bar.dll if |plugin_path| is <last_internal_dir>\foo\bar.dll.
    406             //
    407             // Every iteration the last path component from |plugin_path| is
    408             // removed and prepended to |relative_path| until we get up to
    409             // |last_internal_dir|.
    410             while (last_internal_dir.IsParent(plugin_path)) {
    411               relative_path = plugin_path.BaseName().Append(relative_path);
    412 
    413               base::FilePath old_path = plugin_path;
    414               plugin_path = plugin_path.DirName();
    415               // To be extra sure that we won't end up in an infinite loop.
    416               if (old_path == plugin_path) {
    417                 NOTREACHED();
    418                 break;
    419               }
    420             }
    421 
    422             // If |relative_path| is empty, |plugin_path| is not within
    423             // |last_internal_dir|. We don't need to update it.
    424             if (!relative_path.empty()) {
    425               plugin_path = cur_internal_dir.Append(relative_path);
    426               path = plugin_path.value();
    427               plugin->SetString("path", path);
    428             }
    429           }
    430 
    431           if (migrate_to_pepper_flash &&
    432                      base::FilePath::CompareEqualIgnoreCase(
    433                          path, npapi_flash.value())) {
    434             npapi_flash_enabled = enabled;
    435           } else if (migrate_to_pepper_flash &&
    436                      base::FilePath::CompareEqualIgnoreCase(
    437                          path, pepper_flash.value())) {
    438             if (!enabled)
    439               pepper_flash_node = plugin;
    440           } else if (remove_component_pepper_flash_settings &&
    441                      IsComponentUpdatedPepperFlash(plugin_path)) {
    442             if (!enabled) {
    443               component_pepper_flash_node = it;
    444               // Skip setting |enabled| into |plugin_state_|.
    445               continue;
    446             }
    447           }
    448 
    449           plugin_state_.Set(plugin_path, enabled);
    450         } else if (!enabled && plugin->GetString("name", &group_name)) {
    451           // Otherwise this is a list of groups.
    452           plugin_group_state_[group_name] = false;
    453         }
    454       }
    455 
    456       if (npapi_flash_enabled && pepper_flash_node) {
    457         DCHECK(migrate_to_pepper_flash);
    458         pepper_flash_node->SetBoolean("enabled", true);
    459         plugin_state_.Set(pepper_flash, true);
    460       }
    461 
    462       if (component_pepper_flash_node != saved_plugins_list->end()) {
    463         DCHECK(remove_component_pepper_flash_settings);
    464         saved_plugins_list->Erase(component_pepper_flash_node, NULL);
    465       }
    466     } else {
    467       // If the saved plugin list is empty, then the call to UpdatePreferences()
    468       // below failed in an earlier run, possibly because the user closed the
    469       // browser too quickly.
    470 
    471       // Only want one PDF plugin enabled at a time. See http://crbug.com/50105
    472       // for background.
    473       plugin_group_state_[base::ASCIIToUTF16(
    474           PluginMetadata::kAdobeReaderGroupName)] = false;
    475     }
    476   }  // Scoped update of prefs::kPluginsPluginsList.
    477 
    478   // Build the set of policy enabled/disabled plugin patterns once and cache it.
    479   // Don't do this in the constructor, there's no profile available there.
    480   ListValueToStringSet(prefs_->GetList(prefs::kPluginsDisabledPlugins),
    481                        &policy_disabled_plugin_patterns_);
    482   ListValueToStringSet(
    483       prefs_->GetList(prefs::kPluginsDisabledPluginsExceptions),
    484       &policy_disabled_plugin_exception_patterns_);
    485   ListValueToStringSet(prefs_->GetList(prefs::kPluginsEnabledPlugins),
    486                        &policy_enabled_plugin_patterns_);
    487 
    488   registrar_.Init(prefs_);
    489 
    490   // Because pointers to our own members will remain unchanged for the
    491   // lifetime of |registrar_| (which we also own), we can bind their
    492   // pointer values directly in the callbacks to avoid string-based
    493   // lookups at notification time.
    494   registrar_.Add(prefs::kPluginsDisabledPlugins,
    495                  base::Bind(&PluginPrefs::UpdatePatternsAndNotify,
    496                             base::Unretained(this),
    497                             &policy_disabled_plugin_patterns_));
    498   registrar_.Add(prefs::kPluginsDisabledPluginsExceptions,
    499                  base::Bind(&PluginPrefs::UpdatePatternsAndNotify,
    500                             base::Unretained(this),
    501                             &policy_disabled_plugin_exception_patterns_));
    502   registrar_.Add(prefs::kPluginsEnabledPlugins,
    503                  base::Bind(&PluginPrefs::UpdatePatternsAndNotify,
    504                             base::Unretained(this),
    505                             &policy_enabled_plugin_patterns_));
    506 
    507   NotifyPluginStatusChanged();
    508 }
    509 
    510 void PluginPrefs::ShutdownOnUIThread() {
    511   prefs_ = NULL;
    512   registrar_.RemoveAll();
    513 }
    514 
    515 PluginPrefs::PluginPrefs() : profile_(NULL),
    516                              prefs_(NULL) {
    517 }
    518 
    519 PluginPrefs::~PluginPrefs() {
    520 }
    521 
    522 void PluginPrefs::SetPolicyEnforcedPluginPatterns(
    523     const std::set<base::string16>& disabled_patterns,
    524     const std::set<base::string16>& disabled_exception_patterns,
    525     const std::set<base::string16>& enabled_patterns) {
    526   policy_disabled_plugin_patterns_ = disabled_patterns;
    527   policy_disabled_plugin_exception_patterns_ = disabled_exception_patterns;
    528   policy_enabled_plugin_patterns_ = enabled_patterns;
    529 }
    530 
    531 void PluginPrefs::OnUpdatePreferences(
    532     const std::vector<content::WebPluginInfo>& plugins) {
    533   if (!prefs_)
    534     return;
    535 
    536   PluginFinder* finder = PluginFinder::GetInstance();
    537   ListPrefUpdate update(prefs_, prefs::kPluginsPluginsList);
    538   base::ListValue* plugins_list = update.Get();
    539   plugins_list->Clear();
    540 
    541   base::FilePath internal_dir;
    542   if (PathService::Get(chrome::DIR_INTERNAL_PLUGINS, &internal_dir))
    543     prefs_->SetFilePath(prefs::kPluginsLastInternalDirectory, internal_dir);
    544 
    545   base::AutoLock auto_lock(lock_);
    546 
    547   // Add the plugin files.
    548   std::set<base::string16> group_names;
    549   for (size_t i = 0; i < plugins.size(); ++i) {
    550     base::DictionaryValue* summary = new base::DictionaryValue();
    551     summary->SetString("path", plugins[i].path.value());
    552     summary->SetString("name", plugins[i].name);
    553     summary->SetString("version", plugins[i].version);
    554     bool enabled = true;
    555     plugin_state_.Get(plugins[i].path, &enabled);
    556     summary->SetBoolean("enabled", enabled);
    557     plugins_list->Append(summary);
    558 
    559     scoped_ptr<PluginMetadata> plugin_metadata(
    560         finder->GetPluginMetadata(plugins[i]));
    561     // Insert into a set of all group names.
    562     group_names.insert(plugin_metadata->name());
    563   }
    564 
    565   // Add the plug-in groups.
    566   for (std::set<base::string16>::const_iterator it = group_names.begin();
    567       it != group_names.end(); ++it) {
    568     base::DictionaryValue* summary = new base::DictionaryValue();
    569     summary->SetString("name", *it);
    570     bool enabled = true;
    571     std::map<base::string16, bool>::iterator gstate_it =
    572         plugin_group_state_.find(*it);
    573     if (gstate_it != plugin_group_state_.end())
    574       enabled = gstate_it->second;
    575     summary->SetBoolean("enabled", enabled);
    576     plugins_list->Append(summary);
    577   }
    578 }
    579 
    580 void PluginPrefs::NotifyPluginStatusChanged() {
    581   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    582   content::NotificationService::current()->Notify(
    583       chrome::NOTIFICATION_PLUGIN_ENABLE_STATUS_CHANGED,
    584       content::Source<Profile>(profile_),
    585       content::NotificationService::NoDetails());
    586 }
    587