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