plugin_prefs.cc revision 5821806d5e7f356e8fa4b058a389a808ea183019
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.h"
13#include "base/path_service.h"
14#include "base/string_util.h"
15#include "base/utf_string_conversions.h"
16#include "base/values.h"
17#include "build/build_config.h"
18#include "chrome/browser/plugins/plugin_installer.h"
19#include "chrome/browser/plugins/plugin_metadata.h"
20#include "chrome/browser/plugins/plugin_prefs_factory.h"
21#include "chrome/browser/browser_process.h"
22#include "chrome/browser/prefs/scoped_user_pref_update.h"
23#include "chrome/browser/profiles/profile.h"
24#include "chrome/browser/profiles/profile_keyed_service.h"
25#include "chrome/common/chrome_constants.h"
26#include "chrome/common/chrome_content_client.h"
27#include "chrome/common/chrome_notification_types.h"
28#include "chrome/common/chrome_paths.h"
29#include "chrome/common/chrome_switches.h"
30#include "chrome/common/pref_names.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 "webkit/plugins/npapi/plugin_list.h"
35#include "webkit/plugins/webplugininfo.h"
36
37using content::BrowserThread;
38using content::PluginService;
39
40namespace {
41
42// How long to wait to save the plugin enabled information, which might need to
43// go to disk.
44const int64 kPluginUpdateDelayMs = 60 * 1000;
45
46}  // namespace
47
48PluginPrefs::PluginState::PluginState() {
49}
50
51PluginPrefs::PluginState::~PluginState() {
52}
53
54bool PluginPrefs::PluginState::Get(const FilePath& plugin,
55                                   bool* enabled) const {
56  FilePath key = ConvertMapKey(plugin);
57  std::map<FilePath, bool>::const_iterator iter = state_.find(key);
58  if (iter != state_.end()) {
59    *enabled = iter->second;
60    return true;
61  }
62  return false;
63}
64
65void PluginPrefs::PluginState::Set(const FilePath& plugin, bool enabled) {
66  state_[ConvertMapKey(plugin)] = enabled;
67}
68
69void PluginPrefs::PluginState::SetIgnorePseudoKey(const FilePath& plugin,
70                                                  bool enabled) {
71  FilePath key = ConvertMapKey(plugin);
72  if (key == plugin)
73    state_[key] = enabled;
74}
75
76FilePath PluginPrefs::PluginState::ConvertMapKey(const FilePath& plugin) const {
77  // Keep the state of component-updated and bundled Pepper Flash in sync.
78  if (plugin.BaseName().value() == chrome::kPepperFlashPluginFilename) {
79    FilePath component_updated_pepper_flash_dir;
80    if (PathService::Get(chrome::DIR_COMPONENT_UPDATED_PEPPER_FLASH_PLUGIN,
81                         &component_updated_pepper_flash_dir) &&
82        component_updated_pepper_flash_dir.IsParent(plugin)) {
83      FilePath bundled_pepper_flash;
84      if (PathService::Get(chrome::FILE_PEPPER_FLASH_PLUGIN,
85                           &bundled_pepper_flash)) {
86        return bundled_pepper_flash;
87      }
88    }
89  }
90
91  return plugin;
92}
93
94// static
95scoped_refptr<PluginPrefs> PluginPrefs::GetForProfile(Profile* profile) {
96  return PluginPrefsFactory::GetPrefsForProfile(profile);
97}
98
99// static
100scoped_refptr<PluginPrefs> PluginPrefs::GetForTestingProfile(
101    Profile* profile) {
102  return static_cast<PluginPrefs*>(
103      PluginPrefsFactory::GetInstance()->SetTestingFactoryAndUse(
104          profile, &PluginPrefsFactory::CreateForTestingProfile).get());
105}
106
107void PluginPrefs::SetPluginListForTesting(
108    webkit::npapi::PluginList* plugin_list) {
109  plugin_list_ = plugin_list;
110}
111
112void PluginPrefs::EnablePluginGroup(bool enabled, const string16& group_name) {
113  PluginService::GetInstance()->GetPlugins(
114      base::Bind(&PluginPrefs::EnablePluginGroupInternal,
115                 this, enabled, group_name));
116}
117
118void PluginPrefs::EnablePluginGroupInternal(
119    bool enabled,
120    const string16& group_name,
121    const std::vector<webkit::WebPluginInfo>& plugins) {
122  base::AutoLock auto_lock(lock_);
123  PluginFinder* finder = PluginFinder::GetInstance();
124
125  // Set the desired state for the group.
126  plugin_group_state_[group_name] = enabled;
127
128  // Update the state for all plug-ins in the group.
129  for (size_t i = 0; i < plugins.size(); ++i) {
130    scoped_ptr<PluginMetadata> plugin(finder->GetPluginMetadata(plugins[i]));
131    if (group_name != plugin->name())
132      continue;
133    plugin_state_.Set(plugins[i].path, enabled);
134  }
135
136  BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
137      base::Bind(&PluginPrefs::OnUpdatePreferences, this, plugins));
138  BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
139      base::Bind(&PluginPrefs::NotifyPluginStatusChanged, this));
140}
141
142void PluginPrefs::EnablePlugin(
143    bool enabled, const FilePath& path,
144    const base::Callback<void(bool)>& callback) {
145  PluginFinder* finder = PluginFinder::GetInstance();
146  webkit::WebPluginInfo plugin;
147  bool can_enable = true;
148  if (PluginService::GetInstance()->GetPluginInfoByPath(path, &plugin)) {
149    scoped_ptr<PluginMetadata> plugin_metadata(
150        finder->GetPluginMetadata(plugin));
151    PolicyStatus plugin_status = PolicyStatusForPlugin(plugin.name);
152    PolicyStatus group_status = PolicyStatusForPlugin(plugin_metadata->name());
153    if (enabled) {
154      if (plugin_status == POLICY_DISABLED || group_status == POLICY_DISABLED)
155        can_enable = false;
156    } else {
157      if (plugin_status == POLICY_ENABLED || group_status == POLICY_ENABLED)
158        can_enable = false;
159    }
160  } else {
161    NOTREACHED();
162  }
163
164  if (!can_enable) {
165    MessageLoop::current()->PostTask(FROM_HERE,
166                                     base::Bind(callback, false));
167    return;
168  }
169
170  PluginService::GetInstance()->GetPlugins(
171      base::Bind(&PluginPrefs::EnablePluginInternal, this,
172                 enabled, path, finder, callback));
173}
174
175void PluginPrefs::EnablePluginInternal(
176    bool enabled,
177    const FilePath& path,
178    PluginFinder* plugin_finder,
179    const base::Callback<void(bool)>& callback,
180    const std::vector<webkit::WebPluginInfo>& plugins) {
181  {
182    // Set the desired state for the plug-in.
183    base::AutoLock auto_lock(lock_);
184    plugin_state_.Set(path, enabled);
185  }
186
187  string16 group_name;
188  for (size_t i = 0; i < plugins.size(); ++i) {
189    if (plugins[i].path == path) {
190      scoped_ptr<PluginMetadata> plugin_metadata(
191          plugin_finder->GetPluginMetadata(plugins[i]));
192      // set the group name for this plug-in.
193      group_name = plugin_metadata->name();
194      DCHECK_EQ(enabled, IsPluginEnabled(plugins[i]));
195      break;
196    }
197  }
198
199  bool all_disabled = true;
200  for (size_t i = 0; i < plugins.size(); ++i) {
201    scoped_ptr<PluginMetadata> plugin_metadata(
202        plugin_finder->GetPluginMetadata(plugins[i]));
203    DCHECK(!plugin_metadata->name().empty());
204    if (group_name == plugin_metadata->name()) {
205      all_disabled = all_disabled && !IsPluginEnabled(plugins[i]);
206    }
207  }
208
209  if (!group_name.empty()) {
210    // Update the state for the corresponding plug-in group.
211    base::AutoLock auto_lock(lock_);
212    plugin_group_state_[group_name] = !all_disabled;
213  }
214
215  BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
216      base::Bind(&PluginPrefs::OnUpdatePreferences, this, plugins));
217  BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
218      base::Bind(&PluginPrefs::NotifyPluginStatusChanged, this));
219  callback.Run(true);
220}
221
222PluginPrefs::PolicyStatus PluginPrefs::PolicyStatusForPlugin(
223    const string16& name) const {
224  base::AutoLock auto_lock(lock_);
225  if (IsStringMatchedInSet(name, policy_enabled_plugin_patterns_)) {
226    return POLICY_ENABLED;
227  } else if (IsStringMatchedInSet(name, policy_disabled_plugin_patterns_) &&
228             !IsStringMatchedInSet(
229                 name, policy_disabled_plugin_exception_patterns_)) {
230    return POLICY_DISABLED;
231  } else {
232    return NO_POLICY;
233  }
234}
235
236bool PluginPrefs::IsPluginEnabled(const webkit::WebPluginInfo& plugin) const {
237  scoped_ptr<PluginMetadata> plugin_metadata(
238      PluginFinder::GetInstance()->GetPluginMetadata(plugin));
239  string16 group_name = plugin_metadata->name();
240
241  // Check if the plug-in or its group is enabled by policy.
242  PolicyStatus plugin_status = PolicyStatusForPlugin(plugin.name);
243  PolicyStatus group_status = PolicyStatusForPlugin(group_name);
244  if (plugin_status == POLICY_ENABLED || group_status == POLICY_ENABLED)
245    return true;
246
247  // Check if the plug-in or its group is disabled by policy.
248  if (plugin_status == POLICY_DISABLED || group_status == POLICY_DISABLED)
249    return false;
250
251  // If enabling NaCl, make sure the plugin is also enabled. See bug
252  // http://code.google.com/p/chromium/issues/detail?id=81010 for more
253  // information.
254  // TODO(dspringer): When NaCl is on by default, remove this code.
255  if ((plugin.name ==
256       ASCIIToUTF16(chrome::ChromeContentClient::kNaClPluginName)) &&
257      CommandLine::ForCurrentProcess()->HasSwitch(switches::kEnableNaCl)) {
258    return true;
259  }
260
261  base::AutoLock auto_lock(lock_);
262  // Check user preferences for the plug-in.
263  bool plugin_enabled = false;
264  if (plugin_state_.Get(plugin.path, &plugin_enabled))
265    return plugin_enabled;
266
267  // Check user preferences for the plug-in group.
268  std::map<string16, bool>::const_iterator group_it(
269      plugin_group_state_.find(plugin.name));
270  if (group_it != plugin_group_state_.end())
271    return group_it->second;
272
273  // Default to enabled.
274  return true;
275}
276
277void PluginPrefs::OnPreferenceChanged(PrefServiceBase* service,
278                                      const std::string& pref_name) {
279  DCHECK_EQ(prefs_, service);
280  if (pref_name == prefs::kPluginsDisabledPlugins) {
281    base::AutoLock auto_lock(lock_);
282    ListValueToStringSet(prefs_->GetList(prefs::kPluginsDisabledPlugins),
283                         &policy_disabled_plugin_patterns_);
284  } else if (pref_name == prefs::kPluginsDisabledPluginsExceptions) {
285    base::AutoLock auto_lock(lock_);
286    ListValueToStringSet(
287        prefs_->GetList(prefs::kPluginsDisabledPluginsExceptions),
288        &policy_disabled_plugin_exception_patterns_);
289  } else if (pref_name == prefs::kPluginsEnabledPlugins) {
290    base::AutoLock auto_lock(lock_);
291    ListValueToStringSet(prefs_->GetList(prefs::kPluginsEnabledPlugins),
292                         &policy_enabled_plugin_patterns_);
293  } else {
294    NOTREACHED();
295  }
296  NotifyPluginStatusChanged();
297}
298
299/*static*/
300bool PluginPrefs::IsStringMatchedInSet(const string16& name,
301                                       const std::set<string16>& pattern_set) {
302  std::set<string16>::const_iterator pattern(pattern_set.begin());
303  while (pattern != pattern_set.end()) {
304    if (MatchPattern(name, *pattern))
305      return true;
306    ++pattern;
307  }
308
309  return false;
310}
311
312/* static */
313void PluginPrefs::ListValueToStringSet(const ListValue* src,
314                                       std::set<string16>* dest) {
315  DCHECK(src);
316  DCHECK(dest);
317  dest->clear();
318  ListValue::const_iterator end(src->end());
319  for (ListValue::const_iterator current(src->begin());
320       current != end; ++current) {
321    string16 plugin_name;
322    if ((*current)->GetAsString(&plugin_name)) {
323      dest->insert(plugin_name);
324    }
325  }
326}
327
328void PluginPrefs::SetPrefs(PrefService* prefs) {
329  prefs_ = prefs;
330  bool update_internal_dir = false;
331  FilePath last_internal_dir =
332      prefs_->GetFilePath(prefs::kPluginsLastInternalDirectory);
333  FilePath cur_internal_dir;
334  if (PathService::Get(chrome::DIR_INTERNAL_PLUGINS, &cur_internal_dir) &&
335      cur_internal_dir != last_internal_dir) {
336    update_internal_dir = true;
337    prefs_->SetFilePath(
338        prefs::kPluginsLastInternalDirectory, cur_internal_dir);
339  }
340
341  bool force_enable_internal_pdf = false;
342  bool internal_pdf_enabled = false;
343  string16 pdf_group_name =
344      ASCIIToUTF16(chrome::ChromeContentClient::kPDFPluginName);
345  FilePath pdf_path;
346  PathService::Get(chrome::FILE_PDF_PLUGIN, &pdf_path);
347  FilePath::StringType pdf_path_str = pdf_path.value();
348  if (!prefs_->GetBoolean(prefs::kPluginsEnabledInternalPDF)) {
349    // We switched to the internal pdf plugin being on by default, and so we
350    // need to force it to be enabled.  We only want to do it this once though,
351    // i.e. we don't want to enable it again if the user disables it afterwards.
352    prefs_->SetBoolean(prefs::kPluginsEnabledInternalPDF, true);
353    force_enable_internal_pdf = true;
354  }
355
356  bool force_enable_nacl = false;
357  string16 nacl_group_name =
358      ASCIIToUTF16(chrome::ChromeContentClient::kNaClPluginName);
359  // Since the NaCl Plugin changed names between Chrome 13 and 14, we need to
360  // check for both because either could be stored as the plugin group name.
361  string16 old_nacl_group_name =
362      ASCIIToUTF16(chrome::ChromeContentClient::kNaClOldPluginName);
363  FilePath nacl_path;
364  PathService::Get(chrome::FILE_NACL_PLUGIN, &nacl_path);
365  FilePath::StringType nacl_path_str = nacl_path.value();
366  if (!prefs_->GetBoolean(prefs::kPluginsEnabledNaCl)) {
367    // We switched to the nacl plugin being on by default, and so we need to
368    // force it to be enabled.  We only want to do it this once though, i.e.
369    // we don't want to enable it again if the user disables it afterwards.
370    prefs_->SetBoolean(prefs::kPluginsEnabledNaCl, true);
371    force_enable_nacl = true;
372  }
373
374  bool migrate_to_pepper_flash = false;
375#if defined(OS_WIN)
376  // If bundled NPAPI Flash is enabled while Peppper Flash is disabled, we
377  // would like to turn Pepper Flash on. And we only want to do it once.
378  // TODO(yzshen): Remove all |migrate_to_pepper_flash|-related code after it
379  // has been run once by most users. (Maybe Chrome 24 or Chrome 25.)
380  if (!prefs_->GetBoolean(prefs::kPluginsMigratedToPepperFlash)) {
381    prefs_->SetBoolean(prefs::kPluginsMigratedToPepperFlash, true);
382    migrate_to_pepper_flash = true;
383  }
384#endif
385
386  {  // Scoped update of prefs::kPluginsPluginsList.
387    ListPrefUpdate update(prefs_, prefs::kPluginsPluginsList);
388    ListValue* saved_plugins_list = update.Get();
389    if (saved_plugins_list && !saved_plugins_list->empty()) {
390      // The following four variables are only valid when
391      // |migrate_to_pepper_flash| is set to true.
392      FilePath npapi_flash;
393      FilePath pepper_flash;
394      DictionaryValue* pepper_flash_node = NULL;
395      bool npapi_flash_enabled = false;
396      if (migrate_to_pepper_flash) {
397        PathService::Get(chrome::FILE_FLASH_PLUGIN, &npapi_flash);
398        PathService::Get(chrome::FILE_PEPPER_FLASH_PLUGIN, &pepper_flash);
399      }
400
401      for (ListValue::const_iterator it = saved_plugins_list->begin();
402           it != saved_plugins_list->end();
403           ++it) {
404        if (!(*it)->IsType(Value::TYPE_DICTIONARY)) {
405          LOG(WARNING) << "Invalid entry in " << prefs::kPluginsPluginsList;
406          continue;  // Oops, don't know what to do with this item.
407        }
408
409        DictionaryValue* plugin = static_cast<DictionaryValue*>(*it);
410        string16 group_name;
411        bool enabled;
412        if (!plugin->GetBoolean("enabled", &enabled))
413          enabled = true;
414
415        FilePath::StringType path;
416        // The plugin list constains all the plugin files in addition to the
417        // plugin groups.
418        if (plugin->GetString("path", &path)) {
419          // Files have a path attribute, groups don't.
420          FilePath plugin_path(path);
421
422          // The path to the intenral plugin directory changes everytime Chrome
423          // is auto-updated, since it contains the current version number. For
424          // example, it changes from foobar\Chrome\Application\21.0.1180.83 to
425          // foobar\Chrome\Application\21.0.1180.89.
426          // However, we would like the settings of internal plugins to persist
427          // across Chrome updates. Therefore, we need to recognize those paths
428          // that are within the previous internal plugin directory, and update
429          // them in the prefs accordingly.
430          if (update_internal_dir) {
431            FilePath relative_path;
432
433            // Extract the part of |plugin_path| that is relative to
434            // |last_internal_dir|. For example, |relative_path| will be
435            // foo\bar.dll if |plugin_path| is <last_internal_dir>\foo\bar.dll.
436            //
437            // Every iteration the last path component from |plugin_path| is
438            // removed and prepended to |relative_path| until we get up to
439            // |last_internal_dir|.
440            while (last_internal_dir.IsParent(plugin_path)) {
441              relative_path = plugin_path.BaseName().Append(relative_path);
442
443              FilePath old_path = plugin_path;
444              plugin_path = plugin_path.DirName();
445              // To be extra sure that we won't end up in an infinite loop.
446              if (old_path == plugin_path) {
447                NOTREACHED();
448                break;
449              }
450            }
451
452            // If |relative_path| is empty, |plugin_path| is not within
453            // |last_internal_dir|. We don't need to update it.
454            if (!relative_path.empty()) {
455              plugin_path = cur_internal_dir.Append(relative_path);
456              path = plugin_path.value();
457              plugin->SetString("path", path);
458            }
459          }
460
461          if (FilePath::CompareIgnoreCase(path, pdf_path_str) == 0) {
462            if (!enabled && force_enable_internal_pdf) {
463              enabled = true;
464              plugin->SetBoolean("enabled", true);
465            }
466
467            internal_pdf_enabled = enabled;
468          } else if (FilePath::CompareIgnoreCase(path, nacl_path_str) == 0) {
469            if (!enabled && force_enable_nacl) {
470              enabled = true;
471              plugin->SetBoolean("enabled", true);
472            }
473          } else if (migrate_to_pepper_flash &&
474              FilePath::CompareEqualIgnoreCase(path, npapi_flash.value())) {
475            npapi_flash_enabled = enabled;
476          } else if (migrate_to_pepper_flash &&
477              FilePath::CompareEqualIgnoreCase(path, pepper_flash.value())) {
478            if (!enabled)
479              pepper_flash_node = plugin;
480          }
481
482          plugin_state_.SetIgnorePseudoKey(plugin_path, enabled);
483        } else if (!enabled && plugin->GetString("name", &group_name)) {
484          // Don't disable this group if it's for the pdf or nacl plugins and
485          // we just forced it on.
486          if (force_enable_internal_pdf && pdf_group_name == group_name)
487            continue;
488          if (force_enable_nacl && (nacl_group_name == group_name ||
489                                    old_nacl_group_name == group_name))
490            continue;
491
492          // Otherwise this is a list of groups.
493          plugin_group_state_[group_name] = false;
494        }
495      }
496
497      if (npapi_flash_enabled && pepper_flash_node) {
498        DCHECK(migrate_to_pepper_flash);
499        pepper_flash_node->SetBoolean("enabled", true);
500        plugin_state_.Set(pepper_flash, true);
501      }
502    } else {
503      // If the saved plugin list is empty, then the call to UpdatePreferences()
504      // below failed in an earlier run, possibly because the user closed the
505      // browser too quickly. Try to force enable the internal PDF and nacl
506      // plugins again.
507      force_enable_internal_pdf = true;
508      force_enable_nacl = true;
509    }
510  }  // Scoped update of prefs::kPluginsPluginsList.
511
512  // Build the set of policy enabled/disabled plugin patterns once and cache it.
513  // Don't do this in the constructor, there's no profile available there.
514  ListValueToStringSet(prefs_->GetList(prefs::kPluginsDisabledPlugins),
515                       &policy_disabled_plugin_patterns_);
516  ListValueToStringSet(
517      prefs_->GetList(prefs::kPluginsDisabledPluginsExceptions),
518      &policy_disabled_plugin_exception_patterns_);
519  ListValueToStringSet(prefs_->GetList(prefs::kPluginsEnabledPlugins),
520                       &policy_enabled_plugin_patterns_);
521
522  registrar_.Init(prefs_);
523  registrar_.Add(prefs::kPluginsDisabledPlugins, this);
524  registrar_.Add(prefs::kPluginsDisabledPluginsExceptions, this);
525  registrar_.Add(prefs::kPluginsEnabledPlugins, this);
526
527  if (force_enable_internal_pdf || internal_pdf_enabled) {
528    // See http://crbug.com/50105 for background.
529    plugin_group_state_[ASCIIToUTF16(
530        PluginMetadata::kAdobeReaderGroupName)] = false;
531  }
532
533  if (force_enable_internal_pdf || force_enable_nacl) {
534    // We want to save this, but doing so requires loading the list of plugins,
535    // so do it after a minute as to not impact startup performance.  Note that
536    // plugins are loaded after 30s by the metrics service.
537    BrowserThread::PostDelayedTask(
538        BrowserThread::FILE,
539        FROM_HERE,
540        base::Bind(&PluginPrefs::GetPreferencesDataOnFileThread, this),
541        base::TimeDelta::FromMilliseconds(kPluginUpdateDelayMs));
542  }
543
544  NotifyPluginStatusChanged();
545}
546
547void PluginPrefs::ShutdownOnUIThread() {
548  prefs_ = NULL;
549  registrar_.RemoveAll();
550}
551
552PluginPrefs::PluginPrefs() : profile_(NULL),
553                             prefs_(NULL),
554                             plugin_list_(NULL) {
555}
556
557PluginPrefs::~PluginPrefs() {
558}
559
560void PluginPrefs::SetPolicyEnforcedPluginPatterns(
561    const std::set<string16>& disabled_patterns,
562    const std::set<string16>& disabled_exception_patterns,
563    const std::set<string16>& enabled_patterns) {
564  policy_disabled_plugin_patterns_ = disabled_patterns;
565  policy_disabled_plugin_exception_patterns_ = disabled_exception_patterns;
566  policy_enabled_plugin_patterns_ = enabled_patterns;
567}
568
569webkit::npapi::PluginList* PluginPrefs::GetPluginList() const {
570  if (plugin_list_)
571    return plugin_list_;
572  return PluginService::GetInstance()->GetPluginList();
573}
574
575void PluginPrefs::GetPreferencesDataOnFileThread() {
576  std::vector<webkit::WebPluginInfo> plugins;
577  webkit::npapi::PluginList* plugin_list = GetPluginList();
578  plugin_list->GetPluginsNoRefresh(&plugins);
579
580  BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
581      base::Bind(&PluginPrefs::OnUpdatePreferences, this, plugins));
582}
583
584void PluginPrefs::OnUpdatePreferences(
585    const std::vector<webkit::WebPluginInfo>& plugins) {
586  if (!prefs_)
587    return;
588
589  PluginFinder* finder = PluginFinder::GetInstance();
590  ListPrefUpdate update(prefs_, prefs::kPluginsPluginsList);
591  ListValue* plugins_list = update.Get();
592  plugins_list->Clear();
593
594  FilePath internal_dir;
595  if (PathService::Get(chrome::DIR_INTERNAL_PLUGINS, &internal_dir))
596    prefs_->SetFilePath(prefs::kPluginsLastInternalDirectory, internal_dir);
597
598  base::AutoLock auto_lock(lock_);
599
600  // Add the plugin files.
601  std::set<string16> group_names;
602  for (size_t i = 0; i < plugins.size(); ++i) {
603    DictionaryValue* summary = new DictionaryValue();
604    summary->SetString("path", plugins[i].path.value());
605    summary->SetString("name", plugins[i].name);
606    summary->SetString("version", plugins[i].version);
607    bool enabled = true;
608    plugin_state_.Get(plugins[i].path, &enabled);
609    summary->SetBoolean("enabled", enabled);
610    plugins_list->Append(summary);
611
612    scoped_ptr<PluginMetadata> plugin_metadata(
613        finder->GetPluginMetadata(plugins[i]));
614    // Insert into a set of all group names.
615    group_names.insert(plugin_metadata->name());
616  }
617
618  // Add the plug-in groups.
619  for (std::set<string16>::const_iterator it = group_names.begin();
620      it != group_names.end(); ++it) {
621    DictionaryValue* summary = new DictionaryValue();
622    summary->SetString("name", *it);
623    bool enabled = true;
624    std::map<string16, bool>::iterator gstate_it =
625        plugin_group_state_.find(*it);
626    if (gstate_it != plugin_group_state_.end())
627      enabled = gstate_it->second;
628    summary->SetBoolean("enabled", enabled);
629    plugins_list->Append(summary);
630  }
631}
632
633void PluginPrefs::NotifyPluginStatusChanged() {
634  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
635  content::NotificationService::current()->Notify(
636      chrome::NOTIFICATION_PLUGIN_ENABLE_STATUS_CHANGED,
637      content::Source<Profile>(profile_),
638      content::NotificationService::NoDetails());
639}
640