1// Copyright 2014 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 "extensions/shell/browser/shell_content_browser_client.h"
6
7#include "base/command_line.h"
8#include "content/public/browser/browser_thread.h"
9#include "content/public/browser/render_process_host.h"
10#include "content/public/browser/site_instance.h"
11#include "content/public/common/content_switches.h"
12#include "content/public/common/url_constants.h"
13#include "content/shell/browser/shell_browser_context.h"
14#include "content/shell/browser/shell_devtools_delegate.h"
15#include "extensions/browser/extension_message_filter.h"
16#include "extensions/browser/extension_protocols.h"
17#include "extensions/browser/extension_registry.h"
18#include "extensions/browser/info_map.h"
19#include "extensions/browser/process_map.h"
20#include "extensions/common/constants.h"
21#include "extensions/common/extension.h"
22#include "extensions/common/switches.h"
23#include "extensions/shell/browser/shell_browser_context.h"
24#include "extensions/shell/browser/shell_browser_main_parts.h"
25#include "extensions/shell/browser/shell_extension_system.h"
26#include "url/gurl.h"
27
28#if !defined(DISABLE_NACL)
29#include "components/nacl/browser/nacl_browser.h"
30#include "components/nacl/browser/nacl_host_message_filter.h"
31#include "components/nacl/browser/nacl_process_host.h"
32#include "components/nacl/common/nacl_process_type.h"
33#include "components/nacl/common/nacl_switches.h"
34#include "content/public/browser/browser_child_process_host.h"
35#include "content/public/browser/child_process_data.h"
36#endif
37
38using base::CommandLine;
39using content::BrowserContext;
40using content::BrowserThread;
41
42namespace extensions {
43namespace {
44
45ShellContentBrowserClient* g_instance = NULL;
46
47}  // namespace
48
49ShellContentBrowserClient::ShellContentBrowserClient(
50    ShellBrowserMainDelegate* browser_main_delegate)
51    : browser_main_parts_(NULL), browser_main_delegate_(browser_main_delegate) {
52  DCHECK(!g_instance);
53  g_instance = this;
54}
55
56ShellContentBrowserClient::~ShellContentBrowserClient() {
57  g_instance = NULL;
58}
59
60// static
61ShellContentBrowserClient* ShellContentBrowserClient::Get() {
62  return g_instance;
63}
64
65content::BrowserContext* ShellContentBrowserClient::GetBrowserContext() {
66  return browser_main_parts_->browser_context();
67}
68
69content::BrowserMainParts* ShellContentBrowserClient::CreateBrowserMainParts(
70    const content::MainFunctionParams& parameters) {
71  browser_main_parts_ =
72      new ShellBrowserMainParts(parameters, browser_main_delegate_);
73  return browser_main_parts_;
74}
75
76void ShellContentBrowserClient::RenderProcessWillLaunch(
77    content::RenderProcessHost* host) {
78  int render_process_id = host->GetID();
79  BrowserContext* browser_context = browser_main_parts_->browser_context();
80  host->AddFilter(
81      new ExtensionMessageFilter(render_process_id, browser_context));
82  // PluginInfoMessageFilter is not required because app_shell does not have
83  // the concept of disabled plugins.
84#if !defined(DISABLE_NACL)
85  host->AddFilter(new nacl::NaClHostMessageFilter(
86      render_process_id,
87      browser_context->IsOffTheRecord(),
88      browser_context->GetPath(),
89      browser_context->GetRequestContextForRenderProcess(render_process_id)));
90#endif
91}
92
93bool ShellContentBrowserClient::ShouldUseProcessPerSite(
94    content::BrowserContext* browser_context,
95    const GURL& effective_url) {
96  // This ensures that all render views created for a single app will use the
97  // same render process (see content::SiteInstance::GetProcess). Otherwise the
98  // default behavior of ContentBrowserClient will lead to separate render
99  // processes for the background page and each app window view.
100  return true;
101}
102
103net::URLRequestContextGetter* ShellContentBrowserClient::CreateRequestContext(
104    content::BrowserContext* content_browser_context,
105    content::ProtocolHandlerMap* protocol_handlers,
106    content::URLRequestInterceptorScopedVector request_interceptors) {
107  // Handle only chrome-extension:// requests. app_shell does not support
108  // chrome-extension-resource:// requests (it does not store shared extension
109  // data in its installation directory).
110  InfoMap* extension_info_map =
111      browser_main_parts_->extension_system()->info_map();
112  (*protocol_handlers)[kExtensionScheme] =
113      linked_ptr<net::URLRequestJobFactory::ProtocolHandler>(
114          CreateExtensionProtocolHandler(false /* is_incognito */,
115                                         extension_info_map));
116  // Let content::ShellBrowserContext handle the rest of the setup.
117  return browser_main_parts_->browser_context()->CreateRequestContext(
118      protocol_handlers, request_interceptors.Pass());
119}
120
121bool ShellContentBrowserClient::IsHandledURL(const GURL& url) {
122  if (!url.is_valid())
123    return false;
124  // Keep in sync with ProtocolHandlers added in CreateRequestContext() and in
125  // content::ShellURLRequestContextGetter::GetURLRequestContext().
126  static const char* const kProtocolList[] = {
127      url::kBlobScheme,
128      content::kChromeDevToolsScheme,
129      content::kChromeUIScheme,
130      url::kDataScheme,
131      url::kFileScheme,
132      url::kFileSystemScheme,
133      kExtensionScheme,
134      kExtensionResourceScheme,
135  };
136  for (size_t i = 0; i < arraysize(kProtocolList); ++i) {
137    if (url.scheme() == kProtocolList[i])
138      return true;
139  }
140  return false;
141}
142
143void ShellContentBrowserClient::SiteInstanceGotProcess(
144    content::SiteInstance* site_instance) {
145  // If this isn't an extension renderer there's nothing to do.
146  const Extension* extension = GetExtension(site_instance);
147  if (!extension)
148    return;
149
150  ProcessMap::Get(browser_main_parts_->browser_context())
151      ->Insert(extension->id(),
152               site_instance->GetProcess()->GetID(),
153               site_instance->GetId());
154
155  BrowserThread::PostTask(
156      BrowserThread::IO,
157      FROM_HERE,
158      base::Bind(&InfoMap::RegisterExtensionProcess,
159                 browser_main_parts_->extension_system()->info_map(),
160                 extension->id(),
161                 site_instance->GetProcess()->GetID(),
162                 site_instance->GetId()));
163}
164
165void ShellContentBrowserClient::SiteInstanceDeleting(
166    content::SiteInstance* site_instance) {
167  // If this isn't an extension renderer there's nothing to do.
168  const Extension* extension = GetExtension(site_instance);
169  if (!extension)
170    return;
171
172  ProcessMap::Get(browser_main_parts_->browser_context())
173      ->Remove(extension->id(),
174               site_instance->GetProcess()->GetID(),
175               site_instance->GetId());
176
177  BrowserThread::PostTask(
178      BrowserThread::IO,
179      FROM_HERE,
180      base::Bind(&InfoMap::UnregisterExtensionProcess,
181                 browser_main_parts_->extension_system()->info_map(),
182                 extension->id(),
183                 site_instance->GetProcess()->GetID(),
184                 site_instance->GetId()));
185}
186
187void ShellContentBrowserClient::AppendExtraCommandLineSwitches(
188    CommandLine* command_line,
189    int child_process_id) {
190  std::string process_type =
191      command_line->GetSwitchValueASCII(::switches::kProcessType);
192  if (process_type == ::switches::kRendererProcess)
193    AppendRendererSwitches(command_line);
194}
195
196content::BrowserPpapiHost*
197ShellContentBrowserClient::GetExternalBrowserPpapiHost(int plugin_process_id) {
198#if !defined(DISABLE_NACL)
199  content::BrowserChildProcessHostIterator iter(PROCESS_TYPE_NACL_LOADER);
200  while (!iter.Done()) {
201    nacl::NaClProcessHost* host = static_cast<nacl::NaClProcessHost*>(
202        iter.GetDelegate());
203    if (host->process() &&
204        host->process()->GetData().id == plugin_process_id) {
205      // Found the plugin.
206      return host->browser_ppapi_host();
207    }
208    ++iter;
209  }
210#endif
211  return NULL;
212}
213
214void ShellContentBrowserClient::GetAdditionalAllowedSchemesForFileSystem(
215    std::vector<std::string>* additional_allowed_schemes) {
216  ContentBrowserClient::GetAdditionalAllowedSchemesForFileSystem(
217      additional_allowed_schemes);
218  additional_allowed_schemes->push_back(kExtensionScheme);
219}
220
221void ShellContentBrowserClient::AppendRendererSwitches(
222    CommandLine* command_line) {
223  // TODO(jamescook): Should we check here if the process is in the extension
224  // service process map, or can we assume all renderers are extension
225  // renderers?
226  command_line->AppendSwitch(switches::kExtensionProcess);
227
228#if !defined(DISABLE_NACL)
229  // NOTE: app_shell does not support non-SFI mode, so it does not pass through
230  // SFI switches either here or for the zygote process.
231  static const char* const kSwitchNames[] = {
232    ::switches::kEnableNaClDebug,
233  };
234  command_line->CopySwitchesFrom(*CommandLine::ForCurrentProcess(),
235                                 kSwitchNames,
236                                 arraysize(kSwitchNames));
237#endif  // !defined(DISABLE_NACL)
238}
239
240const Extension* ShellContentBrowserClient::GetExtension(
241    content::SiteInstance* site_instance) {
242  ExtensionRegistry* registry =
243      ExtensionRegistry::Get(site_instance->GetBrowserContext());
244  return registry->enabled_extensions().GetExtensionOrAppByURL(
245      site_instance->GetSiteURL());
246}
247
248content::DevToolsManagerDelegate*
249ShellContentBrowserClient::GetDevToolsManagerDelegate() {
250  return new content::ShellDevToolsManagerDelegate(GetBrowserContext());
251}
252
253}  // namespace extensions
254