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 "content/shell/browser/shell_content_browser_client.h"
6
7#include "base/base_switches.h"
8#include "base/command_line.h"
9#include "base/files/file.h"
10#include "base/files/file_util.h"
11#include "base/path_service.h"
12#include "base/strings/utf_string_conversions.h"
13#include "content/public/browser/render_process_host.h"
14#include "content/public/browser/resource_dispatcher_host.h"
15#include "content/public/browser/storage_partition.h"
16#include "content/public/common/content_switches.h"
17#include "content/public/common/url_constants.h"
18#include "content/public/common/web_preferences.h"
19#include "content/shell/browser/ipc_echo_message_filter.h"
20#include "content/shell/browser/shell.h"
21#include "content/shell/browser/shell_browser_context.h"
22#include "content/shell/browser/shell_browser_main_parts.h"
23#include "content/shell/browser/shell_devtools_delegate.h"
24#include "content/shell/browser/shell_message_filter.h"
25#include "content/shell/browser/shell_net_log.h"
26#include "content/shell/browser/shell_notification_manager.h"
27#include "content/shell/browser/shell_quota_permission_context.h"
28#include "content/shell/browser/shell_resource_dispatcher_host_delegate.h"
29#include "content/shell/browser/shell_web_contents_view_delegate_creator.h"
30#include "content/shell/browser/webkit_test_controller.h"
31#include "content/shell/common/shell_messages.h"
32#include "content/shell/common/shell_switches.h"
33#include "content/shell/common/webkit_test_helpers.h"
34#include "content/shell/geolocation/shell_access_token_store.h"
35#include "net/url_request/url_request_context_getter.h"
36#include "url/gurl.h"
37
38#if defined(OS_ANDROID)
39#include "base/android/path_utils.h"
40#include "components/crash/browser/crash_dump_manager_android.h"
41#include "content/shell/android/shell_descriptors.h"
42#endif
43
44#if defined(OS_POSIX) && !defined(OS_MACOSX)
45#include "base/debug/leak_annotations.h"
46#include "components/crash/app/breakpad_linux.h"
47#include "components/crash/browser/crash_handler_host_linux.h"
48#include "content/public/common/content_descriptors.h"
49#endif
50
51#if defined(OS_WIN)
52#include "content/common/sandbox_win.h"
53#include "sandbox/win/src/sandbox.h"
54#endif
55
56namespace content {
57
58namespace {
59
60ShellContentBrowserClient* g_browser_client;
61bool g_swap_processes_for_redirect = false;
62
63#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
64breakpad::CrashHandlerHostLinux* CreateCrashHandlerHost(
65    const std::string& process_type) {
66  base::FilePath dumps_path =
67      CommandLine::ForCurrentProcess()->GetSwitchValuePath(
68          switches::kCrashDumpsDir);
69  {
70    ANNOTATE_SCOPED_MEMORY_LEAK;
71    breakpad::CrashHandlerHostLinux* crash_handler =
72        new breakpad::CrashHandlerHostLinux(
73            process_type, dumps_path, false);
74    crash_handler->StartUploaderThread();
75    return crash_handler;
76  }
77}
78
79int GetCrashSignalFD(const CommandLine& command_line) {
80  if (!breakpad::IsCrashReporterEnabled())
81    return -1;
82
83  std::string process_type =
84      command_line.GetSwitchValueASCII(switches::kProcessType);
85
86  if (process_type == switches::kRendererProcess) {
87    static breakpad::CrashHandlerHostLinux* crash_handler = NULL;
88    if (!crash_handler)
89      crash_handler = CreateCrashHandlerHost(process_type);
90    return crash_handler->GetDeathSignalSocket();
91  }
92
93  if (process_type == switches::kPluginProcess) {
94    static breakpad::CrashHandlerHostLinux* crash_handler = NULL;
95    if (!crash_handler)
96      crash_handler = CreateCrashHandlerHost(process_type);
97    return crash_handler->GetDeathSignalSocket();
98  }
99
100  if (process_type == switches::kPpapiPluginProcess) {
101    static breakpad::CrashHandlerHostLinux* crash_handler = NULL;
102    if (!crash_handler)
103      crash_handler = CreateCrashHandlerHost(process_type);
104    return crash_handler->GetDeathSignalSocket();
105  }
106
107  if (process_type == switches::kGpuProcess) {
108    static breakpad::CrashHandlerHostLinux* crash_handler = NULL;
109    if (!crash_handler)
110      crash_handler = CreateCrashHandlerHost(process_type);
111    return crash_handler->GetDeathSignalSocket();
112  }
113
114  return -1;
115}
116#endif  // defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
117
118void RequestDesktopNotificationPermissionOnIO(
119    const GURL& source_origin,
120    RenderFrameHost* render_frame_host,
121    const base::Callback<void(blink::WebNotificationPermission)>& callback) {
122  ShellNotificationManager* manager =
123      ShellContentBrowserClient::Get()->GetShellNotificationManager();
124  if (manager)
125    manager->RequestPermission(source_origin, callback);
126  else
127    callback.Run(blink::WebNotificationPermissionAllowed);
128}
129
130}  // namespace
131
132ShellContentBrowserClient* ShellContentBrowserClient::Get() {
133  return g_browser_client;
134}
135
136void ShellContentBrowserClient::SetSwapProcessesForRedirect(bool swap) {
137  g_swap_processes_for_redirect = swap;
138}
139
140ShellContentBrowserClient::ShellContentBrowserClient()
141    : shell_browser_main_parts_(NULL) {
142  DCHECK(!g_browser_client);
143  g_browser_client = this;
144  if (!CommandLine::ForCurrentProcess()->HasSwitch(switches::kDumpRenderTree))
145    return;
146  webkit_source_dir_ = GetWebKitRootDirFilePath();
147}
148
149ShellContentBrowserClient::~ShellContentBrowserClient() {
150  g_browser_client = NULL;
151}
152
153ShellNotificationManager*
154ShellContentBrowserClient::GetShellNotificationManager() {
155  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
156  if (!CommandLine::ForCurrentProcess()->HasSwitch(switches::kDumpRenderTree))
157    return NULL;
158
159  if (!shell_notification_manager_)
160    shell_notification_manager_.reset(new ShellNotificationManager());
161
162  return shell_notification_manager_.get();
163}
164
165BrowserMainParts* ShellContentBrowserClient::CreateBrowserMainParts(
166    const MainFunctionParams& parameters) {
167  shell_browser_main_parts_ = new ShellBrowserMainParts(parameters);
168  return shell_browser_main_parts_;
169}
170
171void ShellContentBrowserClient::RenderProcessWillLaunch(
172    RenderProcessHost* host) {
173  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kExposeIpcEcho))
174    host->AddFilter(new IPCEchoMessageFilter());
175  if (!CommandLine::ForCurrentProcess()->HasSwitch(switches::kDumpRenderTree))
176    return;
177  host->AddFilter(new ShellMessageFilter(
178      host->GetID(),
179      BrowserContext::GetDefaultStoragePartition(browser_context())
180          ->GetDatabaseTracker(),
181      BrowserContext::GetDefaultStoragePartition(browser_context())
182          ->GetQuotaManager(),
183      BrowserContext::GetDefaultStoragePartition(browser_context())
184          ->GetURLRequestContext()));
185  host->Send(new ShellViewMsg_SetWebKitSourceDir(webkit_source_dir_));
186}
187
188net::URLRequestContextGetter* ShellContentBrowserClient::CreateRequestContext(
189    BrowserContext* content_browser_context,
190    ProtocolHandlerMap* protocol_handlers,
191    URLRequestInterceptorScopedVector request_interceptors) {
192  ShellBrowserContext* shell_browser_context =
193      ShellBrowserContextForBrowserContext(content_browser_context);
194  return shell_browser_context->CreateRequestContext(
195      protocol_handlers, request_interceptors.Pass());
196}
197
198net::URLRequestContextGetter*
199ShellContentBrowserClient::CreateRequestContextForStoragePartition(
200    BrowserContext* content_browser_context,
201    const base::FilePath& partition_path,
202    bool in_memory,
203    ProtocolHandlerMap* protocol_handlers,
204    URLRequestInterceptorScopedVector request_interceptors) {
205  ShellBrowserContext* shell_browser_context =
206      ShellBrowserContextForBrowserContext(content_browser_context);
207  return shell_browser_context->CreateRequestContextForStoragePartition(
208      partition_path,
209      in_memory,
210      protocol_handlers,
211      request_interceptors.Pass());
212}
213
214bool ShellContentBrowserClient::IsHandledURL(const GURL& url) {
215  if (!url.is_valid())
216    return false;
217  DCHECK_EQ(url.scheme(), base::StringToLowerASCII(url.scheme()));
218  // Keep in sync with ProtocolHandlers added by
219  // ShellURLRequestContextGetter::GetURLRequestContext().
220  static const char* const kProtocolList[] = {
221      url::kBlobScheme,
222      url::kFileSystemScheme,
223      kChromeUIScheme,
224      kChromeDevToolsScheme,
225      url::kDataScheme,
226      url::kFileScheme,
227  };
228  for (size_t i = 0; i < arraysize(kProtocolList); ++i) {
229    if (url.scheme() == kProtocolList[i])
230      return true;
231  }
232  return false;
233}
234
235void ShellContentBrowserClient::AppendExtraCommandLineSwitches(
236    CommandLine* command_line, int child_process_id) {
237  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kDumpRenderTree))
238    command_line->AppendSwitch(switches::kDumpRenderTree);
239  if (CommandLine::ForCurrentProcess()->HasSwitch(
240      switches::kEnableFontAntialiasing))
241    command_line->AppendSwitch(switches::kEnableFontAntialiasing);
242  if (CommandLine::ForCurrentProcess()->HasSwitch(
243      switches::kExposeInternalsForTesting))
244    command_line->AppendSwitch(switches::kExposeInternalsForTesting);
245  if (CommandLine::ForCurrentProcess()->HasSwitch(
246      switches::kExposeIpcEcho))
247    command_line->AppendSwitch(switches::kExposeIpcEcho);
248  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kStableReleaseMode))
249    command_line->AppendSwitch(switches::kStableReleaseMode);
250  if (CommandLine::ForCurrentProcess()->HasSwitch(
251          switches::kEnableCrashReporter)) {
252    command_line->AppendSwitch(switches::kEnableCrashReporter);
253  }
254  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kCrashDumpsDir)) {
255    command_line->AppendSwitchPath(
256        switches::kCrashDumpsDir,
257        CommandLine::ForCurrentProcess()->GetSwitchValuePath(
258            switches::kCrashDumpsDir));
259  }
260  if (CommandLine::ForCurrentProcess()->HasSwitch(
261          switches::kEnableLeakDetection)) {
262    command_line->AppendSwitchASCII(
263        switches::kEnableLeakDetection,
264        CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
265            switches::kEnableLeakDetection));
266  }
267  if (CommandLine::ForCurrentProcess()->HasSwitch(
268        switches::kRegisterFontFiles)) {
269    command_line->AppendSwitchASCII(
270        switches::kRegisterFontFiles,
271        CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
272            switches::kRegisterFontFiles));
273  }
274}
275
276void ShellContentBrowserClient::OverrideWebkitPrefs(
277    RenderViewHost* render_view_host,
278    const GURL& url,
279    WebPreferences* prefs) {
280  if (!CommandLine::ForCurrentProcess()->HasSwitch(switches::kDumpRenderTree))
281    return;
282  WebKitTestController::Get()->OverrideWebkitPrefs(prefs);
283}
284
285void ShellContentBrowserClient::ResourceDispatcherHostCreated() {
286  resource_dispatcher_host_delegate_.reset(
287      new ShellResourceDispatcherHostDelegate());
288  ResourceDispatcherHost::Get()->SetDelegate(
289      resource_dispatcher_host_delegate_.get());
290}
291
292std::string ShellContentBrowserClient::GetDefaultDownloadName() {
293  return "download";
294}
295
296WebContentsViewDelegate* ShellContentBrowserClient::GetWebContentsViewDelegate(
297    WebContents* web_contents) {
298#if !defined(USE_AURA)
299  return CreateShellWebContentsViewDelegate(web_contents);
300#else
301  return NULL;
302#endif
303}
304
305QuotaPermissionContext*
306ShellContentBrowserClient::CreateQuotaPermissionContext() {
307  return new ShellQuotaPermissionContext();
308}
309
310void ShellContentBrowserClient::RequestDesktopNotificationPermission(
311    const GURL& source_origin,
312    RenderFrameHost* render_frame_host,
313    const base::Callback<void(blink::WebNotificationPermission)>& callback) {
314  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
315  BrowserThread::PostTask(BrowserThread::IO,
316                          FROM_HERE,
317                          base::Bind(&RequestDesktopNotificationPermissionOnIO,
318                              source_origin,
319                              render_frame_host,
320                              callback));
321}
322
323blink::WebNotificationPermission
324ShellContentBrowserClient::CheckDesktopNotificationPermission(
325    const GURL& source_url,
326    ResourceContext* context,
327    int render_process_id) {
328  ShellNotificationManager* manager = GetShellNotificationManager();
329  if (manager)
330    return manager->CheckPermission(source_url);
331
332  return blink::WebNotificationPermissionAllowed;
333}
334
335SpeechRecognitionManagerDelegate*
336    ShellContentBrowserClient::GetSpeechRecognitionManagerDelegate() {
337  return new ShellSpeechRecognitionManagerDelegate();
338}
339
340net::NetLog* ShellContentBrowserClient::GetNetLog() {
341  return shell_browser_main_parts_->net_log();
342}
343
344bool ShellContentBrowserClient::ShouldSwapProcessesForRedirect(
345    ResourceContext* resource_context,
346    const GURL& current_url,
347    const GURL& new_url) {
348  return g_swap_processes_for_redirect;
349}
350
351DevToolsManagerDelegate*
352ShellContentBrowserClient::GetDevToolsManagerDelegate() {
353  return new ShellDevToolsManagerDelegate(browser_context());
354}
355
356#if defined(OS_POSIX) && !defined(OS_MACOSX)
357void ShellContentBrowserClient::GetAdditionalMappedFilesForChildProcess(
358    const CommandLine& command_line,
359    int child_process_id,
360    std::vector<FileDescriptorInfo>* mappings) {
361#if defined(OS_ANDROID)
362  int flags = base::File::FLAG_OPEN | base::File::FLAG_READ;
363  base::FilePath pak_file;
364  bool r = PathService::Get(base::DIR_ANDROID_APP_DATA, &pak_file);
365  CHECK(r);
366  pak_file = pak_file.Append(FILE_PATH_LITERAL("paks"));
367  pak_file = pak_file.Append(FILE_PATH_LITERAL("content_shell.pak"));
368
369  base::File f(pak_file, flags);
370  if (!f.IsValid()) {
371    NOTREACHED() << "Failed to open file when creating renderer process: "
372                 << "content_shell.pak";
373  }
374  mappings->push_back(
375      FileDescriptorInfo(kShellPakDescriptor, base::FileDescriptor(f.Pass())));
376
377  if (breakpad::IsCrashReporterEnabled()) {
378    f = breakpad::CrashDumpManager::GetInstance()->CreateMinidumpFile(
379        child_process_id);
380    if (!f.IsValid()) {
381      LOG(ERROR) << "Failed to create file for minidump, crash reporting will "
382                 << "be disabled for this process.";
383    } else {
384      mappings->push_back(
385          FileDescriptorInfo(kAndroidMinidumpDescriptor,
386                             base::FileDescriptor(f.Pass())));
387    }
388  }
389#else  // !defined(OS_ANDROID)
390  int crash_signal_fd = GetCrashSignalFD(command_line);
391  if (crash_signal_fd >= 0) {
392    mappings->push_back(FileDescriptorInfo(
393        kCrashDumpSignal, base::FileDescriptor(crash_signal_fd, false)));
394  }
395#endif  // defined(OS_ANDROID)
396}
397#endif  // defined(OS_POSIX) && !defined(OS_MACOSX)
398
399#if defined(OS_WIN)
400void ShellContentBrowserClient::PreSpawnRenderer(sandbox::TargetPolicy* policy,
401                                                 bool* success) {
402  // Add sideloaded font files for testing. See also DIR_WINDOWS_FONTS
403  // addition in |StartSandboxedProcess|.
404  std::vector<std::string> font_files = GetSideloadFontFiles();
405  for (std::vector<std::string>::const_iterator i(font_files.begin());
406      i != font_files.end();
407      ++i) {
408    policy->AddRule(sandbox::TargetPolicy::SUBSYS_FILES,
409        sandbox::TargetPolicy::FILES_ALLOW_READONLY,
410        base::UTF8ToWide(*i).c_str());
411  }
412}
413#endif  // OS_WIN
414
415ShellBrowserContext* ShellContentBrowserClient::browser_context() {
416  return shell_browser_main_parts_->browser_context();
417}
418
419ShellBrowserContext*
420    ShellContentBrowserClient::off_the_record_browser_context() {
421  return shell_browser_main_parts_->off_the_record_browser_context();
422}
423
424AccessTokenStore* ShellContentBrowserClient::CreateAccessTokenStore() {
425  return new ShellAccessTokenStore(browser_context());
426}
427
428ShellBrowserContext*
429ShellContentBrowserClient::ShellBrowserContextForBrowserContext(
430    BrowserContext* content_browser_context) {
431  if (content_browser_context == browser_context())
432    return browser_context();
433  DCHECK_EQ(content_browser_context, off_the_record_browser_context());
434  return off_the_record_browser_context();
435}
436
437}  // namespace content
438