Home | History | Annotate | Download | only in browser
      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