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/extensions/extension_service.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 "chrome/common/extensions/manifest_handlers/mime_types_handler.h"
16 #include "content/public/browser/plugin_service.h"
17 #include "content/public/common/pepper_plugin_info.h"
18 #include "extensions/browser/extension_registry.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
PluginManager(content::BrowserContext * context)28 PluginManager::PluginManager(content::BrowserContext* context)
29 : profile_(Profile::FromBrowserContext(context)),
30 extension_registry_observer_(this) {
31 extension_registry_observer_.Add(ExtensionRegistry::Get(profile_));
32 }
33
~PluginManager()34 PluginManager::~PluginManager() {
35 }
36
37 static base::LazyInstance<BrowserContextKeyedAPIFactory<PluginManager> >
38 g_factory = LAZY_INSTANCE_INITIALIZER;
39
40 // static
41 BrowserContextKeyedAPIFactory<PluginManager>*
GetFactoryInstance()42 PluginManager::GetFactoryInstance() {
43 return g_factory.Pointer();
44 }
45
OnExtensionLoaded(content::BrowserContext * browser_context,const Extension * extension)46 void PluginManager::OnExtensionLoaded(content::BrowserContext* browser_context,
47 const Extension* extension) {
48 bool plugins_or_nacl_changed = false;
49 if (PluginInfo::HasPlugins(extension)) {
50 const PluginInfo::PluginVector* plugins = PluginInfo::GetPlugins(extension);
51 CHECK(plugins);
52 plugins_or_nacl_changed = true;
53 for (PluginInfo::PluginVector::const_iterator plugin = plugins->begin();
54 plugin != plugins->end();
55 ++plugin) {
56 PluginService::GetInstance()->RefreshPlugins();
57 PluginService::GetInstance()->AddExtraPluginPath(plugin->path);
58 ChromePluginServiceFilter* filter =
59 ChromePluginServiceFilter::GetInstance();
60 if (plugin->is_public) {
61 filter->RestrictPluginToProfileAndOrigin(
62 plugin->path, profile_, GURL());
63 } else {
64 filter->RestrictPluginToProfileAndOrigin(
65 plugin->path, profile_, extension->url());
66 }
67 }
68 }
69
70 const NaClModuleInfo::List* nacl_modules =
71 NaClModuleInfo::GetNaClModules(extension);
72 if (nacl_modules) {
73 plugins_or_nacl_changed = true;
74 for (NaClModuleInfo::List::const_iterator module = nacl_modules->begin();
75 module != nacl_modules->end();
76 ++module) {
77 RegisterNaClModule(*module);
78 }
79 UpdatePluginListWithNaClModules();
80 }
81
82 const MimeTypesHandler* handler = MimeTypesHandler::GetHandler(extension);
83 if (handler && !handler->handler_url().empty()) {
84 plugins_or_nacl_changed = true;
85
86 content::WebPluginInfo info;
87 info.type = content::WebPluginInfo::PLUGIN_TYPE_BROWSER_PLUGIN;
88 info.name = base::UTF8ToUTF16(handler->extension_id());
89 info.path = base::FilePath::FromUTF8Unsafe(handler->extension_id());
90
91 for (std::set<std::string>::const_iterator mime_type =
92 handler->mime_type_set().begin();
93 mime_type != handler->mime_type_set().end(); ++mime_type) {
94 content::WebPluginMimeType mime_type_info;
95 mime_type_info.mime_type = *mime_type;
96 info.mime_types.push_back(mime_type_info);
97 }
98
99 PluginService::GetInstance()->RefreshPlugins();
100 PluginService::GetInstance()->RegisterInternalPlugin(info, true);
101 }
102
103 if (plugins_or_nacl_changed)
104 PluginService::GetInstance()->PurgePluginListCache(profile_, false);
105 }
106
OnExtensionUnloaded(content::BrowserContext * browser_context,const Extension * extension,UnloadedExtensionInfo::Reason reason)107 void PluginManager::OnExtensionUnloaded(
108 content::BrowserContext* browser_context,
109 const Extension* extension,
110 UnloadedExtensionInfo::Reason reason) {
111 bool plugins_or_nacl_changed = false;
112 if (PluginInfo::HasPlugins(extension)) {
113 const PluginInfo::PluginVector* plugins = PluginInfo::GetPlugins(extension);
114 plugins_or_nacl_changed = true;
115 for (PluginInfo::PluginVector::const_iterator plugin = plugins->begin();
116 plugin != plugins->end();
117 ++plugin) {
118 PluginService::GetInstance()->ForcePluginShutdown(plugin->path);
119 PluginService::GetInstance()->RefreshPlugins();
120 PluginService::GetInstance()->RemoveExtraPluginPath(plugin->path);
121 ChromePluginServiceFilter::GetInstance()->UnrestrictPlugin(plugin->path);
122 }
123 }
124
125 const NaClModuleInfo::List* nacl_modules =
126 NaClModuleInfo::GetNaClModules(extension);
127 if (nacl_modules) {
128 plugins_or_nacl_changed = true;
129 for (NaClModuleInfo::List::const_iterator module = nacl_modules->begin();
130 module != nacl_modules->end();
131 ++module) {
132 UnregisterNaClModule(*module);
133 }
134 UpdatePluginListWithNaClModules();
135 }
136
137 const MimeTypesHandler* handler = MimeTypesHandler::GetHandler(extension);
138 if (handler && !handler->handler_url().empty()) {
139 plugins_or_nacl_changed = true;
140 base::FilePath path =
141 base::FilePath::FromUTF8Unsafe(handler->extension_id());
142 PluginService::GetInstance()->UnregisterInternalPlugin(path);
143 PluginService::GetInstance()->ForcePluginShutdown(path);
144 PluginService::GetInstance()->RefreshPlugins();
145 }
146
147 if (plugins_or_nacl_changed)
148 PluginService::GetInstance()->PurgePluginListCache(profile_, false);
149 }
150
RegisterNaClModule(const NaClModuleInfo & info)151 void PluginManager::RegisterNaClModule(const NaClModuleInfo& info) {
152 DCHECK(FindNaClModule(info.url) == nacl_module_list_.end());
153 nacl_module_list_.push_front(info);
154 }
155
UnregisterNaClModule(const NaClModuleInfo & info)156 void PluginManager::UnregisterNaClModule(const NaClModuleInfo& info) {
157 NaClModuleInfo::List::iterator iter = FindNaClModule(info.url);
158 DCHECK(iter != nacl_module_list_.end());
159 nacl_module_list_.erase(iter);
160 }
161
UpdatePluginListWithNaClModules()162 void PluginManager::UpdatePluginListWithNaClModules() {
163 // An extension has been added which has a nacl_module component, which means
164 // there is a MIME type that module wants to handle, so we need to add that
165 // MIME type to plugins which handle NaCl modules in order to allow the
166 // individual modules to handle these types.
167 base::FilePath path;
168 if (!PathService::Get(chrome::FILE_NACL_PLUGIN, &path))
169 return;
170 const content::PepperPluginInfo* pepper_info =
171 PluginService::GetInstance()->GetRegisteredPpapiPluginInfo(path);
172 if (!pepper_info)
173 return;
174
175 std::vector<content::WebPluginMimeType>::const_iterator mime_iter;
176 // Check each MIME type the plugins handle for the NaCl MIME type.
177 for (mime_iter = pepper_info->mime_types.begin();
178 mime_iter != pepper_info->mime_types.end(); ++mime_iter) {
179 if (mime_iter->mime_type == kNaClPluginMimeType) {
180 // This plugin handles "application/x-nacl".
181
182 PluginService::GetInstance()->UnregisterInternalPlugin(pepper_info->path);
183
184 content::WebPluginInfo info = pepper_info->ToWebPluginInfo();
185
186 for (NaClModuleInfo::List::const_iterator iter =
187 nacl_module_list_.begin();
188 iter != nacl_module_list_.end(); ++iter) {
189 // Add the MIME type specified in the extension to this NaCl plugin,
190 // With an extra "nacl" argument to specify the location of the NaCl
191 // manifest file.
192 content::WebPluginMimeType mime_type_info;
193 mime_type_info.mime_type = iter->mime_type;
194 mime_type_info.additional_param_names.push_back(
195 base::UTF8ToUTF16("nacl"));
196 mime_type_info.additional_param_values.push_back(
197 base::UTF8ToUTF16(iter->url.spec()));
198 info.mime_types.push_back(mime_type_info);
199 }
200
201 PluginService::GetInstance()->RefreshPlugins();
202 PluginService::GetInstance()->RegisterInternalPlugin(info, true);
203 // This plugin has been modified, no need to check the rest of its
204 // types, but continue checking other plugins.
205 break;
206 }
207 }
208 }
209
FindNaClModule(const GURL & url)210 NaClModuleInfo::List::iterator PluginManager::FindNaClModule(const GURL& url) {
211 for (NaClModuleInfo::List::iterator iter = nacl_module_list_.begin();
212 iter != nacl_module_list_.end(); ++iter) {
213 if (iter->url == url)
214 return iter;
215 }
216 return nacl_module_list_.end();
217 }
218
219 } // namespace extensions
220