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