plugin_updater.cc revision dc0f95d653279beabeb9817299e2902918ba123e
1// Copyright (c) 2010 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/plugin_updater.h"
6
7#include <string>
8
9#include "base/message_loop.h"
10#include "base/path_service.h"
11#include "base/scoped_ptr.h"
12#include "base/utf_string_conversions.h"
13#include "base/values.h"
14#include "base/version.h"
15#include "chrome/browser/prefs/pref_service.h"
16#include "chrome/browser/profiles/profile.h"
17#include "chrome/common/chrome_paths.h"
18#include "chrome/common/notification_service.h"
19#include "chrome/common/pepper_plugin_registry.h"
20#include "chrome/common/pref_names.h"
21#include "content/browser/browser_thread.h"
22#include "webkit/plugins/npapi/plugin_list.h"
23#include "webkit/plugins/npapi/webplugininfo.h"
24
25// How long to wait to save the plugin enabled information, which might need to
26// go to disk.
27#define kPluginUpdateDelayMs (60 * 1000)
28
29PluginUpdater::PluginUpdater()
30    : notify_pending_(false) {
31}
32
33DictionaryValue* PluginUpdater::CreatePluginFileSummary(
34    const webkit::npapi::WebPluginInfo& plugin) {
35  DictionaryValue* data = new DictionaryValue();
36  data->SetString("path", plugin.path.value());
37  data->SetString("name", plugin.name);
38  data->SetString("version", plugin.version);
39  data->SetBoolean("enabled", webkit::npapi::IsPluginEnabled(plugin));
40  return data;
41}
42
43// static
44ListValue* PluginUpdater::GetPluginGroupsData() {
45  std::vector<webkit::npapi::PluginGroup> plugin_groups;
46  webkit::npapi::PluginList::Singleton()->GetPluginGroups(true, &plugin_groups);
47
48  // Construct DictionaryValues to return to the UI
49  ListValue* plugin_groups_data = new ListValue();
50  for (size_t i = 0; i < plugin_groups.size(); ++i) {
51    plugin_groups_data->Append(plugin_groups[i].GetDataForUI());
52  }
53  return plugin_groups_data;
54}
55
56void PluginUpdater::EnablePluginGroup(bool enable, const string16& group_name) {
57  webkit::npapi::PluginList::Singleton()->EnableGroup(enable, group_name);
58  NotifyPluginStatusChanged();
59}
60
61void PluginUpdater::EnablePlugin(bool enable,
62                                 const FilePath::StringType& path) {
63  FilePath file_path(path);
64  if (enable)
65    webkit::npapi::PluginList::Singleton()->EnablePlugin(file_path);
66  else
67    webkit::npapi::PluginList::Singleton()->DisablePlugin(file_path);
68
69  NotifyPluginStatusChanged();
70}
71
72void PluginUpdater::Observe(NotificationType type,
73                            const NotificationSource& source,
74                            const NotificationDetails& details) {
75  DCHECK_EQ(NotificationType::PREF_CHANGED, type.value);
76  const std::string* pref_name = Details<std::string>(details).ptr();
77  if (!pref_name) {
78    NOTREACHED();
79    return;
80  }
81  if (*pref_name == prefs::kPluginsDisabledPlugins ||
82      *pref_name == prefs::kPluginsDisabledPluginsExceptions ||
83      *pref_name == prefs::kPluginsEnabledPlugins) {
84    PrefService* pref_service = Source<PrefService>(source).ptr();
85    const ListValue* disabled_list =
86        pref_service->GetList(prefs::kPluginsDisabledPlugins);
87    const ListValue* exceptions_list =
88        pref_service->GetList(prefs::kPluginsDisabledPluginsExceptions);
89    const ListValue* enabled_list =
90        pref_service->GetList(prefs::kPluginsEnabledPlugins);
91    UpdatePluginsStateFromPolicy(disabled_list, exceptions_list, enabled_list);
92  }
93}
94
95void PluginUpdater::UpdatePluginsStateFromPolicy(
96    const ListValue* disabled_list,
97    const ListValue* exceptions_list,
98    const ListValue* enabled_list) {
99  std::set<string16> disabled_plugin_patterns;
100  std::set<string16> disabled_plugin_exception_patterns;
101  std::set<string16> enabled_plugin_patterns;
102
103  ListValueToStringSet(disabled_list, &disabled_plugin_patterns);
104  ListValueToStringSet(exceptions_list, &disabled_plugin_exception_patterns);
105  ListValueToStringSet(enabled_list, &enabled_plugin_patterns);
106
107  webkit::npapi::PluginGroup::SetPolicyEnforcedPluginPatterns(
108      disabled_plugin_patterns,
109      disabled_plugin_exception_patterns,
110      enabled_plugin_patterns);
111
112  NotifyPluginStatusChanged();
113}
114
115void PluginUpdater::ListValueToStringSet(const ListValue* src,
116                                         std::set<string16>* dest) {
117  DCHECK(src);
118  DCHECK(dest);
119  ListValue::const_iterator end(src->end());
120  for (ListValue::const_iterator current(src->begin());
121       current != end; ++current) {
122    string16 plugin_name;
123    if ((*current)->GetAsString(&plugin_name)) {
124      dest->insert(plugin_name);
125    }
126  }
127}
128
129void PluginUpdater::UpdatePluginGroupsStateFromPrefs(Profile* profile) {
130  bool update_internal_dir = false;
131  FilePath last_internal_dir =
132  profile->GetPrefs()->GetFilePath(prefs::kPluginsLastInternalDirectory);
133  FilePath cur_internal_dir;
134  if (PathService::Get(chrome::DIR_INTERNAL_PLUGINS, &cur_internal_dir) &&
135      cur_internal_dir != last_internal_dir) {
136    update_internal_dir = true;
137    profile->GetPrefs()->SetFilePath(
138        prefs::kPluginsLastInternalDirectory, cur_internal_dir);
139  }
140
141  bool force_enable_internal_pdf = false;
142  bool internal_pdf_enabled = false;
143  string16 pdf_group_name = ASCIIToUTF16(PepperPluginRegistry::kPDFPluginName);
144  FilePath pdf_path;
145  PathService::Get(chrome::FILE_PDF_PLUGIN, &pdf_path);
146  FilePath::StringType pdf_path_str = pdf_path.value();
147  if (!profile->GetPrefs()->GetBoolean(prefs::kPluginsEnabledInternalPDF)) {
148    // We switched to the internal pdf plugin being on by default, and so we
149    // need to force it to be enabled.  We only want to do it this once though,
150    // i.e. we don't want to enable it again if the user disables it afterwards.
151    profile->GetPrefs()->SetBoolean(prefs::kPluginsEnabledInternalPDF, true);
152    force_enable_internal_pdf = true;
153  }
154
155  if (ListValue* saved_plugins_list =
156          profile->GetPrefs()->GetMutableList(prefs::kPluginsPluginsList)) {
157    for (ListValue::const_iterator it = saved_plugins_list->begin();
158         it != saved_plugins_list->end();
159         ++it) {
160      if (!(*it)->IsType(Value::TYPE_DICTIONARY)) {
161        LOG(WARNING) << "Invalid entry in " << prefs::kPluginsPluginsList;
162        continue;  // Oops, don't know what to do with this item.
163      }
164
165      DictionaryValue* plugin = static_cast<DictionaryValue*>(*it);
166      string16 group_name;
167      bool enabled = true;
168      plugin->GetBoolean("enabled", &enabled);
169
170      FilePath::StringType path;
171      // The plugin list constains all the plugin files in addition to the
172      // plugin groups.
173      if (plugin->GetString("path", &path)) {
174        // Files have a path attribute, groups don't.
175        FilePath plugin_path(path);
176        if (update_internal_dir &&
177            FilePath::CompareIgnoreCase(plugin_path.DirName().value(),
178                last_internal_dir.value()) == 0) {
179          // If the internal plugin directory has changed and if the plugin
180          // looks internal, update its path in the prefs.
181          plugin_path = cur_internal_dir.Append(plugin_path.BaseName());
182          path = plugin_path.value();
183          plugin->SetString("path", path);
184        }
185
186        if (FilePath::CompareIgnoreCase(path, pdf_path_str) == 0) {
187          if (!enabled && force_enable_internal_pdf) {
188            enabled = true;
189            plugin->SetBoolean("enabled", true);
190          }
191
192          internal_pdf_enabled = enabled;
193        }
194
195        if (!enabled)
196          webkit::npapi::PluginList::Singleton()->DisablePlugin(plugin_path);
197      } else if (!enabled && plugin->GetString("name", &group_name)) {
198        // Don't disable this group if it's for the pdf plugin and we just
199        // forced it on.
200        if (force_enable_internal_pdf && pdf_group_name == group_name)
201          continue;
202
203        // Otherwise this is a list of groups.
204        EnablePluginGroup(false, group_name);
205      }
206    }
207  }
208
209  // Build the set of policy enabled/disabled plugin patterns once and cache it.
210  // Don't do this in the constructor, there's no profile available there.
211  const ListValue* disabled_plugins =
212      profile->GetPrefs()->GetList(prefs::kPluginsDisabledPlugins);
213  const ListValue* disabled_exception_plugins =
214      profile->GetPrefs()->GetList(prefs::kPluginsDisabledPluginsExceptions);
215  const ListValue* enabled_plugins =
216      profile->GetPrefs()->GetList(prefs::kPluginsEnabledPlugins);
217  UpdatePluginsStateFromPolicy(disabled_plugins,
218                               disabled_exception_plugins,
219                               enabled_plugins);
220
221  if (force_enable_internal_pdf || internal_pdf_enabled) {
222    // See http://crbug.com/50105 for background.
223    EnablePluginGroup(false, ASCIIToUTF16(
224        webkit::npapi::PluginGroup::kAdobeReaderGroupName));
225  }
226
227  if (force_enable_internal_pdf) {
228    // We want to save this, but doing so requires loading the list of plugins,
229    // so do it after a minute as to not impact startup performance.  Note that
230    // plugins are loaded after 30s by the metrics service.
231    UpdatePreferences(profile, kPluginUpdateDelayMs);
232  }
233}
234
235void PluginUpdater::UpdatePreferences(Profile* profile, int delay_ms) {
236  BrowserThread::PostDelayedTask(
237    BrowserThread::FILE,
238    FROM_HERE,
239    NewRunnableFunction(
240        &PluginUpdater::GetPreferencesDataOnFileThread, profile), delay_ms);
241}
242
243void PluginUpdater::GetPreferencesDataOnFileThread(void* profile) {
244  std::vector<webkit::npapi::WebPluginInfo> plugins;
245  webkit::npapi::PluginList::Singleton()->GetPlugins(false, &plugins);
246
247  std::vector<webkit::npapi::PluginGroup> groups;
248  webkit::npapi::PluginList::Singleton()->GetPluginGroups(false, &groups);
249
250  BrowserThread::PostTask(
251    BrowserThread::UI,
252    FROM_HERE,
253    NewRunnableFunction(&PluginUpdater::OnUpdatePreferences,
254                        static_cast<Profile*>(profile),
255                        plugins, groups));
256}
257
258void PluginUpdater::OnUpdatePreferences(
259    Profile* profile,
260    const std::vector<webkit::npapi::WebPluginInfo>& plugins,
261    const std::vector<webkit::npapi::PluginGroup>& groups) {
262  ListValue* plugins_list = profile->GetPrefs()->GetMutableList(
263      prefs::kPluginsPluginsList);
264  plugins_list->Clear();
265
266  FilePath internal_dir;
267  if (PathService::Get(chrome::DIR_INTERNAL_PLUGINS, &internal_dir))
268    profile->GetPrefs()->SetFilePath(prefs::kPluginsLastInternalDirectory,
269                                     internal_dir);
270
271  // Add the plugin files.
272  for (size_t i = 0; i < plugins.size(); ++i) {
273    DictionaryValue* summary = CreatePluginFileSummary(plugins[i]);
274    // If the plugin is managed by policy, store the user preferred state
275    // instead.
276    if (plugins[i].enabled & webkit::npapi::WebPluginInfo::MANAGED_MASK) {
277      bool user_enabled =
278          (plugins[i].enabled & webkit::npapi::WebPluginInfo::USER_MASK) ==
279              webkit::npapi::WebPluginInfo::USER_ENABLED;
280      summary->SetBoolean("enabled", user_enabled);
281    }
282    bool enabled_val;
283    summary->GetBoolean("enabled", &enabled_val);
284    plugins_list->Append(summary);
285  }
286
287  // Add the groups as well.
288  for (size_t i = 0; i < groups.size(); ++i) {
289      DictionaryValue* summary = groups[i].GetSummary();
290      // If the plugin is disabled only by policy don't store this state in the
291      // user pref store.
292      if (!groups[i].Enabled() &&
293          webkit::npapi::PluginGroup::IsPluginNameDisabledByPolicy(
294              groups[i].GetGroupName()))
295        summary->SetBoolean("enabled", true);
296      plugins_list->Append(summary);
297  }
298}
299
300void PluginUpdater::NotifyPluginStatusChanged() {
301  if (notify_pending_)
302    return;
303  notify_pending_ = true;
304  MessageLoop::current()->PostTask(
305      FROM_HERE,
306      NewRunnableFunction(&PluginUpdater::OnNotifyPluginStatusChanged));
307}
308
309void PluginUpdater::OnNotifyPluginStatusChanged() {
310  GetInstance()->notify_pending_ = false;
311  NotificationService::current()->Notify(
312      NotificationType::PLUGIN_ENABLE_STATUS_CHANGED,
313      Source<PluginUpdater>(GetInstance()),
314      NotificationService::NoDetails());
315}
316
317/*static*/
318PluginUpdater* PluginUpdater::GetInstance() {
319  return Singleton<PluginUpdater>::get();
320}
321