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