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