Home | History | Annotate | Download | only in common
      1 // Copyright (c) 2012 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 "content/common/plugin_list.h"
      6 
      7 #include <algorithm>
      8 
      9 #include "base/command_line.h"
     10 #include "base/lazy_instance.h"
     11 #include "base/logging.h"
     12 #include "base/strings/string_split.h"
     13 #include "base/strings/string_util.h"
     14 #include "base/strings/sys_string_conversions.h"
     15 #include "base/strings/utf_string_conversions.h"
     16 #include "content/public/common/content_switches.h"
     17 #include "net/base/mime_util.h"
     18 #include "url/gurl.h"
     19 
     20 #if defined(OS_WIN)
     21 #include "content/common/plugin_constants_win.h"
     22 #endif
     23 
     24 namespace content {
     25 
     26 namespace {
     27 
     28 base::LazyInstance<PluginList> g_singleton = LAZY_INSTANCE_INITIALIZER;
     29 
     30 }  // namespace
     31 
     32 // static
     33 PluginList* PluginList::Singleton() {
     34   return g_singleton.Pointer();
     35 }
     36 
     37 // static
     38 bool PluginList::DebugPluginLoading() {
     39   return base::CommandLine::ForCurrentProcess()->HasSwitch(
     40       switches::kDebugPluginLoading);
     41 }
     42 
     43 void PluginList::DisablePluginsDiscovery() {
     44   plugins_discovery_disabled_ = true;
     45 }
     46 
     47 void PluginList::RefreshPlugins() {
     48   base::AutoLock lock(lock_);
     49   loading_state_ = LOADING_STATE_NEEDS_REFRESH;
     50 }
     51 
     52 void PluginList::AddExtraPluginPath(const base::FilePath& plugin_path) {
     53   // Chrome OS only loads plugins from /opt/google/chrome/plugins.
     54 #if !defined(OS_CHROMEOS)
     55   base::AutoLock lock(lock_);
     56   extra_plugin_paths_.push_back(plugin_path);
     57 #endif
     58 }
     59 
     60 void PluginList::RemoveExtraPluginPath(const base::FilePath& plugin_path) {
     61   base::AutoLock lock(lock_);
     62   RemoveExtraPluginPathLocked(plugin_path);
     63 }
     64 
     65 void PluginList::AddExtraPluginDir(const base::FilePath& plugin_dir) {
     66   // Chrome OS only loads plugins from /opt/google/chrome/plugins.
     67 #if !defined(OS_CHROMEOS)
     68   base::AutoLock lock(lock_);
     69   extra_plugin_dirs_.push_back(plugin_dir);
     70 #endif
     71 }
     72 
     73 void PluginList::RegisterInternalPlugin(const WebPluginInfo& info,
     74                                         bool add_at_beginning) {
     75   base::AutoLock lock(lock_);
     76 
     77   internal_plugins_.push_back(info);
     78   if (add_at_beginning) {
     79     // Newer registrations go earlier in the list so they can override the MIME
     80     // types of older registrations.
     81     extra_plugin_paths_.insert(extra_plugin_paths_.begin(), info.path);
     82   } else {
     83     extra_plugin_paths_.push_back(info.path);
     84   }
     85 }
     86 
     87 void PluginList::UnregisterInternalPlugin(const base::FilePath& path) {
     88   base::AutoLock lock(lock_);
     89   bool found = false;
     90   for (size_t i = 0; i < internal_plugins_.size(); i++) {
     91     if (internal_plugins_[i].path == path) {
     92       internal_plugins_.erase(internal_plugins_.begin() + i);
     93       found = true;
     94       break;
     95     }
     96   }
     97   DCHECK(found);
     98   RemoveExtraPluginPathLocked(path);
     99 }
    100 
    101 void PluginList::GetInternalPlugins(
    102     std::vector<WebPluginInfo>* internal_plugins) {
    103   base::AutoLock lock(lock_);
    104 
    105   for (std::vector<WebPluginInfo>::iterator it = internal_plugins_.begin();
    106        it != internal_plugins_.end();
    107        ++it) {
    108     internal_plugins->push_back(*it);
    109   }
    110 }
    111 
    112 bool PluginList::ReadPluginInfo(const base::FilePath& filename,
    113                                 WebPluginInfo* info) {
    114   {
    115     base::AutoLock lock(lock_);
    116     for (size_t i = 0; i < internal_plugins_.size(); ++i) {
    117       if (filename == internal_plugins_[i].path) {
    118         *info = internal_plugins_[i];
    119         return true;
    120       }
    121     }
    122   }
    123 
    124   return PluginList::ReadWebPluginInfo(filename, info);
    125 }
    126 
    127 // static
    128 bool PluginList::ParseMimeTypes(
    129     const std::string& mime_types_str,
    130     const std::string& file_extensions_str,
    131     const base::string16& mime_type_descriptions_str,
    132     std::vector<WebPluginMimeType>* parsed_mime_types) {
    133   std::vector<std::string> mime_types, file_extensions;
    134   std::vector<base::string16> descriptions;
    135   base::SplitString(mime_types_str, '|', &mime_types);
    136   base::SplitString(file_extensions_str, '|', &file_extensions);
    137   base::SplitString(mime_type_descriptions_str, '|', &descriptions);
    138 
    139   parsed_mime_types->clear();
    140 
    141   if (mime_types.empty())
    142     return false;
    143 
    144   for (size_t i = 0; i < mime_types.size(); ++i) {
    145     WebPluginMimeType mime_type;
    146     mime_type.mime_type = base::StringToLowerASCII(mime_types[i]);
    147     if (file_extensions.size() > i)
    148       base::SplitString(file_extensions[i], ',', &mime_type.file_extensions);
    149 
    150     if (descriptions.size() > i) {
    151       mime_type.description = descriptions[i];
    152 
    153       // On Windows, the description likely has a list of file extensions
    154       // embedded in it (e.g. "SurfWriter file (*.swr)"). Remove an extension
    155       // list from the description if it is present.
    156       size_t ext = mime_type.description.find(base::ASCIIToUTF16("(*"));
    157       if (ext != base::string16::npos) {
    158         if (ext > 1 && mime_type.description[ext - 1] == ' ')
    159           ext--;
    160 
    161         mime_type.description.erase(ext);
    162       }
    163     }
    164 
    165     parsed_mime_types->push_back(mime_type);
    166   }
    167 
    168   return true;
    169 }
    170 
    171 PluginList::PluginList()
    172     : loading_state_(LOADING_STATE_NEEDS_REFRESH),
    173       plugins_discovery_disabled_(false) {
    174 }
    175 
    176 bool PluginList::PrepareForPluginLoading() {
    177   base::AutoLock lock(lock_);
    178   if (loading_state_ == LOADING_STATE_UP_TO_DATE)
    179     return false;
    180 
    181   loading_state_ = LOADING_STATE_REFRESHING;
    182   return true;
    183 }
    184 
    185 void PluginList::LoadPlugins(bool include_npapi) {
    186   if (!PrepareForPluginLoading())
    187     return;
    188 
    189   std::vector<WebPluginInfo> new_plugins;
    190   base::Closure will_load_callback;
    191   {
    192     base::AutoLock lock(lock_);
    193     will_load_callback = will_load_plugins_callback_;
    194   }
    195   if (!will_load_callback.is_null())
    196     will_load_callback.Run();
    197 
    198   std::vector<base::FilePath> plugin_paths;
    199   GetPluginPathsToLoad(&plugin_paths, include_npapi);
    200 
    201   for (std::vector<base::FilePath>::const_iterator it = plugin_paths.begin();
    202        it != plugin_paths.end();
    203        ++it) {
    204     WebPluginInfo plugin_info;
    205     LoadPluginIntoPluginList(*it, &new_plugins, &plugin_info);
    206   }
    207 
    208   SetPlugins(new_plugins);
    209 }
    210 
    211 bool PluginList::LoadPluginIntoPluginList(
    212     const base::FilePath& path,
    213     std::vector<WebPluginInfo>* plugins,
    214     WebPluginInfo* plugin_info) {
    215   LOG_IF(ERROR, PluginList::DebugPluginLoading())
    216       << "Loading plugin " << path.value();
    217   if (!ReadPluginInfo(path, plugin_info))
    218     return false;
    219 
    220   if (!ShouldLoadPluginUsingPluginList(*plugin_info, plugins))
    221     return false;
    222 
    223 #if defined(OS_WIN) && !defined(NDEBUG)
    224   if (path.BaseName().value() != L"npspy.dll")  // Make an exception for NPSPY
    225 #endif
    226   {
    227     for (size_t i = 0; i < plugin_info->mime_types.size(); ++i) {
    228       // TODO: don't load global handlers for now.
    229       // WebKit hands to the Plugin before it tries
    230       // to handle mimeTypes on its own.
    231       const std::string &mime_type = plugin_info->mime_types[i].mime_type;
    232       if (mime_type == "*")
    233         return false;
    234     }
    235   }
    236   plugins->push_back(*plugin_info);
    237   return true;
    238 }
    239 
    240 void PluginList::GetPluginPathsToLoad(std::vector<base::FilePath>* plugin_paths,
    241                                       bool include_npapi) {
    242   // Don't want to hold the lock while loading new plugins, so we don't block
    243   // other methods if they're called on other threads.
    244   std::vector<base::FilePath> extra_plugin_paths;
    245   std::vector<base::FilePath> extra_plugin_dirs;
    246   {
    247     base::AutoLock lock(lock_);
    248     extra_plugin_paths = extra_plugin_paths_;
    249     extra_plugin_dirs = extra_plugin_dirs_;
    250   }
    251 
    252   for (size_t i = 0; i < extra_plugin_paths.size(); ++i) {
    253     const base::FilePath& path = extra_plugin_paths[i];
    254     if (std::find(plugin_paths->begin(), plugin_paths->end(), path) !=
    255         plugin_paths->end()) {
    256       continue;
    257     }
    258     plugin_paths->push_back(path);
    259   }
    260 
    261   if (include_npapi) {
    262     // A bit confusingly, this function is used to load Pepper plugins as well.
    263     // Those are all internal plugins so we have to use extra_plugin_paths.
    264     for (size_t i = 0; i < extra_plugin_dirs.size(); ++i)
    265       GetPluginsInDir(extra_plugin_dirs[i], plugin_paths);
    266 
    267     std::vector<base::FilePath> directories_to_scan;
    268     GetPluginDirectories(&directories_to_scan);
    269     for (size_t i = 0; i < directories_to_scan.size(); ++i)
    270       GetPluginsInDir(directories_to_scan[i], plugin_paths);
    271 
    272 #if defined(OS_WIN)
    273     GetPluginPathsFromRegistry(plugin_paths);
    274 #endif
    275   }
    276 }
    277 
    278 void PluginList::SetPlugins(const std::vector<WebPluginInfo>& plugins) {
    279   base::AutoLock lock(lock_);
    280 
    281   // If we haven't been invalidated in the mean time, mark the plug-in list as
    282   // up-to-date.
    283   if (loading_state_ != LOADING_STATE_NEEDS_REFRESH)
    284     loading_state_ = LOADING_STATE_UP_TO_DATE;
    285 
    286   plugins_list_ = plugins;
    287 }
    288 
    289 void PluginList::set_will_load_plugins_callback(const base::Closure& callback) {
    290   base::AutoLock lock(lock_);
    291   will_load_plugins_callback_ = callback;
    292 }
    293 
    294 void PluginList::GetPlugins(std::vector<WebPluginInfo>* plugins,
    295                             bool include_npapi) {
    296   LoadPlugins(include_npapi);
    297   base::AutoLock lock(lock_);
    298   plugins->insert(plugins->end(), plugins_list_.begin(), plugins_list_.end());
    299 }
    300 
    301 bool PluginList::GetPluginsNoRefresh(std::vector<WebPluginInfo>* plugins) {
    302   base::AutoLock lock(lock_);
    303   plugins->insert(plugins->end(), plugins_list_.begin(), plugins_list_.end());
    304 
    305   return loading_state_ == LOADING_STATE_UP_TO_DATE;
    306 }
    307 
    308 void PluginList::GetPluginInfoArray(
    309     const GURL& url,
    310     const std::string& mime_type,
    311     bool allow_wildcard,
    312     bool* use_stale,
    313     bool include_npapi,
    314     std::vector<WebPluginInfo>* info,
    315     std::vector<std::string>* actual_mime_types) {
    316   DCHECK(mime_type == base::StringToLowerASCII(mime_type));
    317   DCHECK(info);
    318 
    319   if (!use_stale)
    320     LoadPlugins(include_npapi);
    321   base::AutoLock lock(lock_);
    322   if (use_stale)
    323     *use_stale = (loading_state_ != LOADING_STATE_UP_TO_DATE);
    324   info->clear();
    325   if (actual_mime_types)
    326     actual_mime_types->clear();
    327 
    328   std::set<base::FilePath> visited_plugins;
    329 
    330   // Add in plugins by mime type.
    331   for (size_t i = 0; i < plugins_list_.size(); ++i) {
    332     if (SupportsType(plugins_list_[i], mime_type, allow_wildcard)) {
    333       base::FilePath path = plugins_list_[i].path;
    334       if (visited_plugins.insert(path).second) {
    335         info->push_back(plugins_list_[i]);
    336         if (actual_mime_types)
    337           actual_mime_types->push_back(mime_type);
    338       }
    339     }
    340   }
    341 
    342   // Add in plugins by url.
    343   // We do not permit URL-sniff based plug-in MIME type overrides aside from
    344   // the case where the "type" was initially missing.
    345   // We collected stats to determine this approach isn't a major compat issue,
    346   // and we defend against content confusion attacks in various cases, such
    347   // as when the user doesn't have the Flash plug-in enabled.
    348   std::string path = url.path();
    349   std::string::size_type last_dot = path.rfind('.');
    350   if (last_dot != std::string::npos && mime_type.empty()) {
    351     std::string extension =
    352         base::StringToLowerASCII(std::string(path, last_dot+1));
    353     std::string actual_mime_type;
    354     for (size_t i = 0; i < plugins_list_.size(); ++i) {
    355       if (SupportsExtension(plugins_list_[i], extension, &actual_mime_type)) {
    356         base::FilePath path = plugins_list_[i].path;
    357         if (visited_plugins.insert(path).second) {
    358           info->push_back(plugins_list_[i]);
    359           if (actual_mime_types)
    360             actual_mime_types->push_back(actual_mime_type);
    361         }
    362       }
    363     }
    364   }
    365 }
    366 
    367 bool PluginList::SupportsType(const WebPluginInfo& plugin,
    368                               const std::string& mime_type,
    369                               bool allow_wildcard) {
    370   // Webkit will ask for a plugin to handle empty mime types.
    371   if (mime_type.empty())
    372     return false;
    373 
    374   for (size_t i = 0; i < plugin.mime_types.size(); ++i) {
    375     const WebPluginMimeType& mime_info = plugin.mime_types[i];
    376     if (net::MatchesMimeType(mime_info.mime_type, mime_type)) {
    377       if (!allow_wildcard && mime_info.mime_type == "*")
    378         continue;
    379       return true;
    380     }
    381   }
    382   return false;
    383 }
    384 
    385 bool PluginList::SupportsExtension(const WebPluginInfo& plugin,
    386                                    const std::string& extension,
    387                                    std::string* actual_mime_type) {
    388   for (size_t i = 0; i < plugin.mime_types.size(); ++i) {
    389     const WebPluginMimeType& mime_type = plugin.mime_types[i];
    390     for (size_t j = 0; j < mime_type.file_extensions.size(); ++j) {
    391       if (mime_type.file_extensions[j] == extension) {
    392         if (actual_mime_type)
    393           *actual_mime_type = mime_type.mime_type;
    394         return true;
    395       }
    396     }
    397   }
    398   return false;
    399 }
    400 
    401 void PluginList::RemoveExtraPluginPathLocked(
    402     const base::FilePath& plugin_path) {
    403   lock_.AssertAcquired();
    404   std::vector<base::FilePath>::iterator it =
    405       std::find(extra_plugin_paths_.begin(), extra_plugin_paths_.end(),
    406                 plugin_path);
    407   if (it != extra_plugin_paths_.end())
    408     extra_plugin_paths_.erase(it);
    409 }
    410 
    411 PluginList::~PluginList() {
    412 }
    413 
    414 
    415 }  // namespace content
    416