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 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 = 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(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 void PluginList::LoadPlugins(bool include_npapi) {
    177   {
    178     base::AutoLock lock(lock_);
    179     if (loading_state_ == LOADING_STATE_UP_TO_DATE)
    180       return;
    181 
    182     loading_state_ = LOADING_STATE_REFRESHING;
    183   }
    184 
    185   std::vector<WebPluginInfo> new_plugins;
    186   base::Closure will_load_callback;
    187   {
    188     base::AutoLock lock(lock_);
    189     will_load_callback = will_load_plugins_callback_;
    190   }
    191   if (!will_load_callback.is_null())
    192     will_load_callback.Run();
    193 
    194   std::vector<base::FilePath> plugin_paths;
    195   GetPluginPathsToLoad(&plugin_paths, include_npapi);
    196 
    197   for (std::vector<base::FilePath>::const_iterator it = plugin_paths.begin();
    198        it != plugin_paths.end();
    199        ++it) {
    200     WebPluginInfo plugin_info;
    201     LoadPluginIntoPluginList(*it, &new_plugins, &plugin_info);
    202   }
    203 
    204   base::AutoLock lock(lock_);
    205   plugins_list_.swap(new_plugins);
    206 
    207   // If we haven't been invalidated in the mean time, mark the plug-in list as
    208   // up-to-date.
    209   if (loading_state_ != LOADING_STATE_NEEDS_REFRESH)
    210     loading_state_ = LOADING_STATE_UP_TO_DATE;
    211 }
    212 
    213 bool PluginList::LoadPluginIntoPluginList(
    214     const base::FilePath& path,
    215     std::vector<WebPluginInfo>* plugins,
    216     WebPluginInfo* plugin_info) {
    217   LOG_IF(ERROR, PluginList::DebugPluginLoading())
    218       << "Loading plugin " << path.value();
    219   if (!ReadPluginInfo(path, plugin_info))
    220     return false;
    221 
    222   if (!ShouldLoadPluginUsingPluginList(*plugin_info, plugins))
    223     return false;
    224 
    225 #if defined(OS_WIN) && !defined(NDEBUG)
    226   if (path.BaseName().value() != L"npspy.dll")  // Make an exception for NPSPY
    227 #endif
    228   {
    229     for (size_t i = 0; i < plugin_info->mime_types.size(); ++i) {
    230       // TODO: don't load global handlers for now.
    231       // WebKit hands to the Plugin before it tries
    232       // to handle mimeTypes on its own.
    233       const std::string &mime_type = plugin_info->mime_types[i].mime_type;
    234       if (mime_type == "*")
    235         return false;
    236     }
    237   }
    238   plugins->push_back(*plugin_info);
    239   return true;
    240 }
    241 
    242 void PluginList::GetPluginPathsToLoad(std::vector<base::FilePath>* plugin_paths,
    243                                       bool include_npapi) {
    244   // Don't want to hold the lock while loading new plugins, so we don't block
    245   // other methods if they're called on other threads.
    246   std::vector<base::FilePath> extra_plugin_paths;
    247   std::vector<base::FilePath> extra_plugin_dirs;
    248   {
    249     base::AutoLock lock(lock_);
    250     extra_plugin_paths = extra_plugin_paths_;
    251     extra_plugin_dirs = extra_plugin_dirs_;
    252   }
    253 
    254   for (size_t i = 0; i < extra_plugin_paths.size(); ++i) {
    255     const base::FilePath& path = extra_plugin_paths[i];
    256     if (std::find(plugin_paths->begin(), plugin_paths->end(), path) !=
    257         plugin_paths->end()) {
    258       continue;
    259     }
    260     plugin_paths->push_back(path);
    261   }
    262 
    263   if (include_npapi) {
    264     // A bit confusingly, this function is used to load Pepper plugins as well.
    265     // Those are all internal plugins so we have to use extra_plugin_paths.
    266     for (size_t i = 0; i < extra_plugin_dirs.size(); ++i)
    267       GetPluginsInDir(extra_plugin_dirs[i], plugin_paths);
    268 
    269     std::vector<base::FilePath> directories_to_scan;
    270     GetPluginDirectories(&directories_to_scan);
    271     for (size_t i = 0; i < directories_to_scan.size(); ++i)
    272       GetPluginsInDir(directories_to_scan[i], plugin_paths);
    273 
    274 #if defined(OS_WIN)
    275     GetPluginPathsFromRegistry(plugin_paths);
    276 #endif
    277   }
    278 }
    279 
    280 void PluginList::SetPlugins(const std::vector<WebPluginInfo>& plugins) {
    281   base::AutoLock lock(lock_);
    282 
    283   DCHECK_NE(LOADING_STATE_REFRESHING, loading_state_);
    284   loading_state_ = LOADING_STATE_UP_TO_DATE;
    285 
    286   plugins_list_.clear();
    287   plugins_list_.insert(plugins_list_.end(), plugins.begin(), plugins.end());
    288 }
    289 
    290 void PluginList::set_will_load_plugins_callback(const base::Closure& callback) {
    291   base::AutoLock lock(lock_);
    292   will_load_plugins_callback_ = callback;
    293 }
    294 
    295 void PluginList::GetPlugins(std::vector<WebPluginInfo>* plugins,
    296                             bool include_npapi) {
    297   LoadPlugins(include_npapi);
    298   base::AutoLock lock(lock_);
    299   plugins->insert(plugins->end(), plugins_list_.begin(), plugins_list_.end());
    300 }
    301 
    302 bool PluginList::GetPluginsNoRefresh(std::vector<WebPluginInfo>* plugins) {
    303   base::AutoLock lock(lock_);
    304   plugins->insert(plugins->end(), plugins_list_.begin(), plugins_list_.end());
    305 
    306   return loading_state_ == LOADING_STATE_UP_TO_DATE;
    307 }
    308 
    309 void PluginList::GetPluginInfoArray(
    310     const GURL& url,
    311     const std::string& mime_type,
    312     bool allow_wildcard,
    313     bool* use_stale,
    314     bool include_npapi,
    315     std::vector<WebPluginInfo>* info,
    316     std::vector<std::string>* actual_mime_types) {
    317   DCHECK(mime_type == StringToLowerASCII(mime_type));
    318   DCHECK(info);
    319 
    320   if (!use_stale)
    321     LoadPlugins(include_npapi);
    322   base::AutoLock lock(lock_);
    323   if (use_stale)
    324     *use_stale = (loading_state_ != LOADING_STATE_UP_TO_DATE);
    325   info->clear();
    326   if (actual_mime_types)
    327     actual_mime_types->clear();
    328 
    329   std::set<base::FilePath> visited_plugins;
    330 
    331   // Add in plugins by mime type.
    332   for (size_t i = 0; i < plugins_list_.size(); ++i) {
    333     if (SupportsType(plugins_list_[i], mime_type, allow_wildcard)) {
    334       base::FilePath path = plugins_list_[i].path;
    335       if (visited_plugins.insert(path).second) {
    336         info->push_back(plugins_list_[i]);
    337         if (actual_mime_types)
    338           actual_mime_types->push_back(mime_type);
    339       }
    340     }
    341   }
    342 
    343   // Add in plugins by url.
    344   // We do not permit URL-sniff based plug-in MIME type overrides aside from
    345   // the case where the "type" was initially missing.
    346   // We collected stats to determine this approach isn't a major compat issue,
    347   // and we defend against content confusion attacks in various cases, such
    348   // as when the user doesn't have the Flash plug-in enabled.
    349   std::string path = url.path();
    350   std::string::size_type last_dot = path.rfind('.');
    351   if (last_dot != std::string::npos && mime_type.empty()) {
    352     std::string extension = 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