Home | History | Annotate | Download | only in extensions
      1 // Copyright 2013 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 "base/files/file_path.h"
      6 #include "base/lazy_instance.h"
      7 #include "base/path_service.h"
      8 #include "base/strings/utf_string_conversions.h"
      9 #include "chrome/browser/chrome_notification_types.h"
     10 #include "chrome/browser/extensions/plugin_manager.h"
     11 #include "chrome/browser/plugins/chrome_plugin_service_filter.h"
     12 #include "chrome/browser/profiles/profile.h"
     13 #include "chrome/common/chrome_paths.h"
     14 #include "chrome/common/extensions/api/plugins/plugins_handler.h"
     15 #include "content/public/browser/notification_details.h"
     16 #include "content/public/browser/notification_source.h"
     17 #include "content/public/browser/plugin_service.h"
     18 #include "content/public/common/pepper_plugin_info.h"
     19 #include "extensions/common/extension.h"
     20 #include "url/gurl.h"
     21 
     22 using content::PluginService;
     23 
     24 static const char* kNaClPluginMimeType = "application/x-nacl";
     25 
     26 namespace extensions {
     27 
     28 PluginManager::PluginManager(Profile* profile) : profile_(profile) {
     29   registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_LOADED,
     30                  content::Source<Profile>(profile));
     31   registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED,
     32                  content::Source<Profile>(profile));
     33 }
     34 
     35 PluginManager::~PluginManager() {
     36 }
     37 
     38 static base::LazyInstance<ProfileKeyedAPIFactory<PluginManager> >
     39     g_factory = LAZY_INSTANCE_INITIALIZER;
     40 
     41 // static
     42 ProfileKeyedAPIFactory<PluginManager>* PluginManager::GetFactoryInstance() {
     43   return &g_factory.Get();
     44 }
     45 
     46 void PluginManager::Observe(int type,
     47                             const content::NotificationSource& source,
     48                             const content::NotificationDetails& details) {
     49   if (type == chrome::NOTIFICATION_EXTENSION_LOADED) {
     50     const Extension* extension =
     51         content::Details<const Extension>(details).ptr();
     52 
     53     bool plugins_or_nacl_changed = false;
     54     if (PluginInfo::HasPlugins(extension)) {
     55       const PluginInfo::PluginVector* plugins =
     56           PluginInfo::GetPlugins(extension);
     57       CHECK(plugins);
     58       plugins_or_nacl_changed = true;
     59       for (PluginInfo::PluginVector::const_iterator plugin = plugins->begin();
     60            plugin != plugins->end(); ++plugin) {
     61         PluginService::GetInstance()->RefreshPlugins();
     62         PluginService::GetInstance()->AddExtraPluginPath(plugin->path);
     63         ChromePluginServiceFilter* filter =
     64             ChromePluginServiceFilter::GetInstance();
     65         if (plugin->is_public) {
     66           filter->RestrictPluginToProfileAndOrigin(
     67               plugin->path, profile_, GURL());
     68         } else {
     69           filter->RestrictPluginToProfileAndOrigin(
     70               plugin->path, profile_, extension->url());
     71         }
     72       }
     73     }
     74 
     75     const NaClModuleInfo::List* nacl_modules =
     76         NaClModuleInfo::GetNaClModules(extension);
     77     if (nacl_modules) {
     78       plugins_or_nacl_changed = true;
     79       for (NaClModuleInfo::List::const_iterator module = nacl_modules->begin();
     80            module != nacl_modules->end(); ++module) {
     81         RegisterNaClModule(*module);
     82       }
     83       UpdatePluginListWithNaClModules();
     84     }
     85 
     86     if (plugins_or_nacl_changed)
     87       PluginService::GetInstance()->PurgePluginListCache(profile_, false);
     88 
     89   } else if (type == chrome::NOTIFICATION_EXTENSION_UNLOADED) {
     90     const Extension* extension =
     91         content::Details<UnloadedExtensionInfo>(details)->extension;
     92 
     93     bool plugins_or_nacl_changed = false;
     94     if (PluginInfo::HasPlugins(extension)) {
     95       const PluginInfo::PluginVector* plugins =
     96           PluginInfo::GetPlugins(extension);
     97       plugins_or_nacl_changed = true;
     98       for (PluginInfo::PluginVector::const_iterator plugin = plugins->begin();
     99            plugin != plugins->end(); ++plugin) {
    100         PluginService::GetInstance()->ForcePluginShutdown(plugin->path);
    101         PluginService::GetInstance()->RefreshPlugins();
    102         PluginService::GetInstance()->RemoveExtraPluginPath(plugin->path);
    103         ChromePluginServiceFilter::GetInstance()->UnrestrictPlugin(
    104             plugin->path);
    105       }
    106     }
    107 
    108     const NaClModuleInfo::List* nacl_modules =
    109         NaClModuleInfo::GetNaClModules(extension);
    110     if (nacl_modules) {
    111       plugins_or_nacl_changed = true;
    112       for (NaClModuleInfo::List::const_iterator module = nacl_modules->begin();
    113            module != nacl_modules->end(); ++module) {
    114         UnregisterNaClModule(*module);
    115       }
    116       UpdatePluginListWithNaClModules();
    117     }
    118 
    119     if (plugins_or_nacl_changed)
    120       PluginService::GetInstance()->PurgePluginListCache(profile_, false);
    121 
    122   } else {
    123     NOTREACHED();
    124   }
    125 }
    126 
    127 void PluginManager::RegisterNaClModule(const NaClModuleInfo& info) {
    128   DCHECK(FindNaClModule(info.url) == nacl_module_list_.end());
    129   nacl_module_list_.push_front(info);
    130 }
    131 
    132 void PluginManager::UnregisterNaClModule(const NaClModuleInfo& info) {
    133   NaClModuleInfo::List::iterator iter = FindNaClModule(info.url);
    134   DCHECK(iter != nacl_module_list_.end());
    135   nacl_module_list_.erase(iter);
    136 }
    137 
    138 void PluginManager::UpdatePluginListWithNaClModules() {
    139   // An extension has been added which has a nacl_module component, which means
    140   // there is a MIME type that module wants to handle, so we need to add that
    141   // MIME type to plugins which handle NaCl modules in order to allow the
    142   // individual modules to handle these types.
    143   base::FilePath path;
    144   if (!PathService::Get(chrome::FILE_NACL_PLUGIN, &path))
    145     return;
    146   const content::PepperPluginInfo* pepper_info =
    147       PluginService::GetInstance()->GetRegisteredPpapiPluginInfo(path);
    148   if (!pepper_info)
    149     return;
    150 
    151   std::vector<content::WebPluginMimeType>::const_iterator mime_iter;
    152   // Check each MIME type the plugins handle for the NaCl MIME type.
    153   for (mime_iter = pepper_info->mime_types.begin();
    154        mime_iter != pepper_info->mime_types.end(); ++mime_iter) {
    155     if (mime_iter->mime_type == kNaClPluginMimeType) {
    156       // This plugin handles "application/x-nacl".
    157 
    158       PluginService::GetInstance()->UnregisterInternalPlugin(pepper_info->path);
    159 
    160       content::WebPluginInfo info = pepper_info->ToWebPluginInfo();
    161 
    162       for (NaClModuleInfo::List::const_iterator iter =
    163                nacl_module_list_.begin();
    164            iter != nacl_module_list_.end(); ++iter) {
    165         // Add the MIME type specified in the extension to this NaCl plugin,
    166         // With an extra "nacl" argument to specify the location of the NaCl
    167         // manifest file.
    168         content::WebPluginMimeType mime_type_info;
    169         mime_type_info.mime_type = iter->mime_type;
    170         mime_type_info.additional_param_names.push_back(UTF8ToUTF16("nacl"));
    171         mime_type_info.additional_param_values.push_back(
    172             UTF8ToUTF16(iter->url.spec()));
    173         info.mime_types.push_back(mime_type_info);
    174       }
    175 
    176       PluginService::GetInstance()->RefreshPlugins();
    177       PluginService::GetInstance()->RegisterInternalPlugin(info, true);
    178       // This plugin has been modified, no need to check the rest of its
    179       // types, but continue checking other plugins.
    180       break;
    181     }
    182   }
    183 }
    184 
    185 NaClModuleInfo::List::iterator PluginManager::FindNaClModule(const GURL& url) {
    186   for (NaClModuleInfo::List::iterator iter = nacl_module_list_.begin();
    187        iter != nacl_module_list_.end(); ++iter) {
    188     if (iter->url == url)
    189       return iter;
    190   }
    191   return nacl_module_list_.end();
    192 }
    193 
    194 }  // namespace extensions
    195