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 30 PluginUpdater::PluginUpdater() 31 : notify_pending_(false) { 32 } 33 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 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 57 void PluginUpdater::EnablePluginGroup(bool enable, const string16& group_name) { 58 webkit::npapi::PluginList::Singleton()->EnableGroup(enable, group_name); 59 NotifyPluginStatusChanged(); 60 } 61 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 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 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 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 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 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 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 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 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 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*/ 323 PluginUpdater* PluginUpdater::GetInstance() { 324 return Singleton<PluginUpdater>::get(); 325 } 326