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_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"
     35 
     36 using content::BrowserThread;
     37 using content::PluginService;
     38 
     39 namespace {
     40 
     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;
     48     }
     49   }
     50 
     51   return false;
     52 }
     53 
     54 }  // namespace
     55 
     56 PluginPrefs::PluginState::PluginState() {
     57 }
     58 
     59 PluginPrefs::PluginState::~PluginState() {
     60 }
     61 
     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;
     69   }
     70   return false;
     71 }
     72 
     73 void PluginPrefs::PluginState::Set(const base::FilePath& plugin, bool enabled) {
     74   state_[ConvertMapKey(plugin)] = enabled;
     75 }
     76 
     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;
     85     }
     86   }
     87 
     88   return plugin;
     89 }
     90 
     91 // static
     92 scoped_refptr<PluginPrefs> PluginPrefs::GetForProfile(Profile* profile) {
     93   return PluginPrefsFactory::GetPrefsForProfile(profile);
     94 }
     95 
     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());
    102 }
    103 
    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));
    109 }
    110 
    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();
    117 
    118   // Set the desired state for the group.
    119   plugin_group_state_[group_name] = enabled;
    120 
    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);
    127   }
    128 
    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));
    133 }
    134 
    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;
    152     }
    153   } else {
    154     NOTREACHED();
    155   }
    156 
    157   if (!can_enable) {
    158     base::MessageLoop::current()->PostTask(FROM_HERE,
    159                                            base::Bind(callback, false));
    160     return;
    161   }
    162 
    163   PluginService::GetInstance()->GetPlugins(
    164       base::Bind(&PluginPrefs::EnablePluginInternal, this,
    165                  enabled, path, finder, callback));
    166 }
    167 
    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) {
    174   {
    175     // Set the desired state for the plug-in.
    176     base::AutoLock auto_lock(lock_);
    177     plugin_state_.Set(path, enabled);
    178   }
    179 
    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;
    189     }
    190   }
    191 
    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]);
    199     }
    200   }
    201 
    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;
    206   }
    207 
    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);
    213 }
    214 
    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;
    226   }
    227 }
    228 
    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();
    233 
    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;
    239 
    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;
    243 
    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;
    252   }
    253 
    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;
    259 
    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;
    265 
    266   // Default to enabled.
    267   return true;
    268 }
    269 
    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);
    274 
    275   NotifyPluginStatusChanged();
    276 }
    277 
    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;
    287   }
    288 
    289   return false;
    290 }
    291 
    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);
    304     }
    305   }
    306 }
    307 
    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);
    319   }
    320 
    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;
    331   }
    332 #endif
    333 
    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;
    345   }
    346 
    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);
    360       }
    361 
    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();
    365 
    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.
    372         }
    373 
    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;
    380 
    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);
    387 
    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;
    398 
    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.
    402             //
    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);
    408 
    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;
    415               }
    416             }
    417 
    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);
    424             }
    425           }
    426 
    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;
    442             }
    443           }
    444 
    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;
    449         }
    450       }
    451 
    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);
    456       }
    457 
    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);
    461       }
    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.
    466 
    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;
    471     }
    472   }  // Scoped update of prefs::kPluginsPluginsList.
    473 
    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_);
    483 
    484   registrar_.Init(prefs_);
    485 
    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_));
    502 
    503   NotifyPluginStatusChanged();
    504 }
    505 
    506 void PluginPrefs::ShutdownOnUIThread() {
    507   prefs_ = NULL;
    508   registrar_.RemoveAll();
    509 }
    510 
    511 PluginPrefs::PluginPrefs() : profile_(NULL),
    512                              prefs_(NULL) {
    513 }
    514 
    515 PluginPrefs::~PluginPrefs() {
    516 }
    517 
    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;
    525 }
    526 
    527 void PluginPrefs::OnUpdatePreferences(
    528     const std::vector<content::WebPluginInfo>& plugins) {
    529   if (!prefs_)
    530     return;
    531 
    532   PluginFinder* finder = PluginFinder::GetInstance();
    533   ListPrefUpdate update(prefs_, prefs::kPluginsPluginsList);
    534   base::ListValue* plugins_list = update.Get();
    535   plugins_list->Clear();
    536 
    537   base::FilePath internal_dir;
    538   if (PathService::Get(chrome::DIR_INTERNAL_PLUGINS, &internal_dir))
    539     prefs_->SetFilePath(prefs::kPluginsLastInternalDirectory, internal_dir);
    540 
    541   base::AutoLock auto_lock(lock_);
    542 
    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);
    554 
    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());
    559   }
    560 
    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);
    573   }
    574 }
    575 
    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());
    582 }
    583