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