// Copyright 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "content/shell/browser/shell_content_browser_client.h" #include "base/base_switches.h" #include "base/command_line.h" #include "base/files/file.h" #include "base/files/file_util.h" #include "base/path_service.h" #include "base/strings/utf_string_conversions.h" #include "content/public/browser/client_certificate_delegate.h" #include "content/public/browser/page_navigator.h" #include "content/public/browser/render_process_host.h" #include "content/public/browser/resource_dispatcher_host.h" #include "content/public/browser/storage_partition.h" #include "content/public/common/content_switches.h" #include "content/public/common/url_constants.h" #include "content/public/common/web_preferences.h" #include "content/shell/browser/blink_test_controller.h" #include "content/shell/browser/ipc_echo_message_filter.h" #include "content/shell/browser/layout_test/layout_test_browser_main_parts.h" #include "content/shell/browser/layout_test/layout_test_resource_dispatcher_host_delegate.h" #include "content/shell/browser/shell.h" #include "content/shell/browser/shell_access_token_store.h" #include "content/shell/browser/shell_browser_context.h" #include "content/shell/browser/shell_browser_main_parts.h" #include "content/shell/browser/shell_devtools_manager_delegate.h" #include "content/shell/browser/shell_net_log.h" #include "content/shell/browser/shell_quota_permission_context.h" #include "content/shell/browser/shell_resource_dispatcher_host_delegate.h" #include "content/shell/browser/shell_web_contents_view_delegate_creator.h" #include "content/shell/common/shell_messages.h" #include "content/shell/common/shell_switches.h" #include "content/shell/renderer/layout_test/blink_test_helpers.h" #include "gin/v8_initializer.h" #include "net/url_request/url_request_context_getter.h" #include "url/gurl.h" #if defined(OS_ANDROID) #include "base/android/path_utils.h" #include "components/crash/browser/crash_dump_manager_android.h" #include "content/shell/android/shell_descriptors.h" #endif #if defined(OS_POSIX) && !defined(OS_MACOSX) #include "base/debug/leak_annotations.h" #include "components/crash/app/breakpad_linux.h" #include "components/crash/browser/crash_handler_host_linux.h" #include "content/public/common/content_descriptors.h" #endif #if defined(OS_WIN) #include "content/common/sandbox_win.h" #include "sandbox/win/src/sandbox.h" #endif namespace content { namespace { ShellContentBrowserClient* g_browser_client; bool g_swap_processes_for_redirect = false; #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID) breakpad::CrashHandlerHostLinux* CreateCrashHandlerHost( const std::string& process_type) { base::FilePath dumps_path = base::CommandLine::ForCurrentProcess()->GetSwitchValuePath( switches::kCrashDumpsDir); { ANNOTATE_SCOPED_MEMORY_LEAK; breakpad::CrashHandlerHostLinux* crash_handler = new breakpad::CrashHandlerHostLinux( process_type, dumps_path, false); crash_handler->StartUploaderThread(); return crash_handler; } } int GetCrashSignalFD(const base::CommandLine& command_line) { if (!breakpad::IsCrashReporterEnabled()) return -1; std::string process_type = command_line.GetSwitchValueASCII(switches::kProcessType); if (process_type == switches::kRendererProcess) { static breakpad::CrashHandlerHostLinux* crash_handler = NULL; if (!crash_handler) crash_handler = CreateCrashHandlerHost(process_type); return crash_handler->GetDeathSignalSocket(); } if (process_type == switches::kPluginProcess) { static breakpad::CrashHandlerHostLinux* crash_handler = NULL; if (!crash_handler) crash_handler = CreateCrashHandlerHost(process_type); return crash_handler->GetDeathSignalSocket(); } if (process_type == switches::kPpapiPluginProcess) { static breakpad::CrashHandlerHostLinux* crash_handler = NULL; if (!crash_handler) crash_handler = CreateCrashHandlerHost(process_type); return crash_handler->GetDeathSignalSocket(); } if (process_type == switches::kGpuProcess) { static breakpad::CrashHandlerHostLinux* crash_handler = NULL; if (!crash_handler) crash_handler = CreateCrashHandlerHost(process_type); return crash_handler->GetDeathSignalSocket(); } return -1; } #endif // defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID) } // namespace ShellContentBrowserClient* ShellContentBrowserClient::Get() { return g_browser_client; } void ShellContentBrowserClient::SetSwapProcessesForRedirect(bool swap) { g_swap_processes_for_redirect = swap; } ShellContentBrowserClient::ShellContentBrowserClient() : #if defined(OS_POSIX) && !defined(OS_MACOSX) v8_natives_fd_(-1), v8_snapshot_fd_(-1), #endif // OS_POSIX && !OS_MACOSX shell_browser_main_parts_(NULL) { DCHECK(!g_browser_client); g_browser_client = this; } ShellContentBrowserClient::~ShellContentBrowserClient() { g_browser_client = NULL; } BrowserMainParts* ShellContentBrowserClient::CreateBrowserMainParts( const MainFunctionParams& parameters) { shell_browser_main_parts_ = base::CommandLine::ForCurrentProcess()->HasSwitch( switches::kRunLayoutTest) ? new LayoutTestBrowserMainParts(parameters) : new ShellBrowserMainParts(parameters); return shell_browser_main_parts_; } void ShellContentBrowserClient::RenderProcessWillLaunch( RenderProcessHost* host) { if (base::CommandLine::ForCurrentProcess()->HasSwitch( switches::kExposeIpcEcho)) host->AddFilter(new IPCEchoMessageFilter()); } net::URLRequestContextGetter* ShellContentBrowserClient::CreateRequestContext( BrowserContext* content_browser_context, ProtocolHandlerMap* protocol_handlers, URLRequestInterceptorScopedVector request_interceptors) { ShellBrowserContext* shell_browser_context = ShellBrowserContextForBrowserContext(content_browser_context); return shell_browser_context->CreateRequestContext( protocol_handlers, request_interceptors.Pass()); } net::URLRequestContextGetter* ShellContentBrowserClient::CreateRequestContextForStoragePartition( BrowserContext* content_browser_context, const base::FilePath& partition_path, bool in_memory, ProtocolHandlerMap* protocol_handlers, URLRequestInterceptorScopedVector request_interceptors) { ShellBrowserContext* shell_browser_context = ShellBrowserContextForBrowserContext(content_browser_context); return shell_browser_context->CreateRequestContextForStoragePartition( partition_path, in_memory, protocol_handlers, request_interceptors.Pass()); } bool ShellContentBrowserClient::IsHandledURL(const GURL& url) { if (!url.is_valid()) return false; DCHECK_EQ(url.scheme(), base::StringToLowerASCII(url.scheme())); // Keep in sync with ProtocolHandlers added by // ShellURLRequestContextGetter::GetURLRequestContext(). static const char* const kProtocolList[] = { url::kBlobScheme, url::kFileSystemScheme, kChromeUIScheme, kChromeDevToolsScheme, url::kDataScheme, url::kFileScheme, }; for (size_t i = 0; i < arraysize(kProtocolList); ++i) { if (url.scheme() == kProtocolList[i]) return true; } return false; } void ShellContentBrowserClient::AppendExtraCommandLineSwitches( base::CommandLine* command_line, int child_process_id) { #if defined(OS_POSIX) && !defined(OS_MACOSX) #if defined(V8_USE_EXTERNAL_STARTUP_DATA) std::string process_type = command_line->GetSwitchValueASCII(switches::kProcessType); if (process_type != switches::kZygoteProcess) { command_line->AppendSwitch(::switches::kV8NativesPassedByFD); command_line->AppendSwitch(::switches::kV8SnapshotPassedByFD); } #endif // V8_USE_EXTERNAL_STARTUP_DATA #endif // OS_POSIX && !OS_MACOSX if (base::CommandLine::ForCurrentProcess()->HasSwitch( switches::kRunLayoutTest)) command_line->AppendSwitch(switches::kRunLayoutTest); if (base::CommandLine::ForCurrentProcess()->HasSwitch( switches::kEnableFontAntialiasing)) command_line->AppendSwitch(switches::kEnableFontAntialiasing); if (base::CommandLine::ForCurrentProcess()->HasSwitch( switches::kExposeInternalsForTesting)) command_line->AppendSwitch(switches::kExposeInternalsForTesting); if (base::CommandLine::ForCurrentProcess()->HasSwitch( switches::kExposeIpcEcho)) command_line->AppendSwitch(switches::kExposeIpcEcho); if (base::CommandLine::ForCurrentProcess()->HasSwitch( switches::kStableReleaseMode)) command_line->AppendSwitch(switches::kStableReleaseMode); if (base::CommandLine::ForCurrentProcess()->HasSwitch( switches::kEnableCrashReporter)) { command_line->AppendSwitch(switches::kEnableCrashReporter); } if (base::CommandLine::ForCurrentProcess()->HasSwitch( switches::kCrashDumpsDir)) { command_line->AppendSwitchPath( switches::kCrashDumpsDir, base::CommandLine::ForCurrentProcess()->GetSwitchValuePath( switches::kCrashDumpsDir)); } if (base::CommandLine::ForCurrentProcess()->HasSwitch( switches::kEnableLeakDetection)) { command_line->AppendSwitchASCII( switches::kEnableLeakDetection, base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( switches::kEnableLeakDetection)); } if (base::CommandLine::ForCurrentProcess()->HasSwitch( switches::kRegisterFontFiles)) { command_line->AppendSwitchASCII( switches::kRegisterFontFiles, base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( switches::kRegisterFontFiles)); } } void ShellContentBrowserClient::OverrideWebkitPrefs( RenderViewHost* render_view_host, WebPreferences* prefs) { if (!base::CommandLine::ForCurrentProcess()->HasSwitch( switches::kRunLayoutTest)) return; BlinkTestController::Get()->OverrideWebkitPrefs(prefs); } void ShellContentBrowserClient::ResourceDispatcherHostCreated() { resource_dispatcher_host_delegate_.reset( base::CommandLine::ForCurrentProcess()->HasSwitch( switches::kRunLayoutTest) ? new LayoutTestResourceDispatcherHostDelegate : new ShellResourceDispatcherHostDelegate); ResourceDispatcherHost::Get()->SetDelegate( resource_dispatcher_host_delegate_.get()); } std::string ShellContentBrowserClient::GetDefaultDownloadName() { return "download"; } WebContentsViewDelegate* ShellContentBrowserClient::GetWebContentsViewDelegate( WebContents* web_contents) { #if !defined(USE_AURA) return CreateShellWebContentsViewDelegate(web_contents); #else return NULL; #endif } QuotaPermissionContext* ShellContentBrowserClient::CreateQuotaPermissionContext() { return new ShellQuotaPermissionContext(); } void ShellContentBrowserClient::SelectClientCertificate( WebContents* web_contents, net::SSLCertRequestInfo* cert_request_info, scoped_ptr delegate) { if (!select_client_certificate_callback_.is_null()) select_client_certificate_callback_.Run(); } SpeechRecognitionManagerDelegate* ShellContentBrowserClient::CreateSpeechRecognitionManagerDelegate() { return new ShellSpeechRecognitionManagerDelegate(); } net::NetLog* ShellContentBrowserClient::GetNetLog() { return shell_browser_main_parts_->net_log(); } bool ShellContentBrowserClient::ShouldSwapProcessesForRedirect( ResourceContext* resource_context, const GURL& current_url, const GURL& new_url) { return g_swap_processes_for_redirect; } DevToolsManagerDelegate* ShellContentBrowserClient::GetDevToolsManagerDelegate() { return new ShellDevToolsManagerDelegate(browser_context()); } void ShellContentBrowserClient::OpenURL( BrowserContext* browser_context, const OpenURLParams& params, const base::Callback& callback) { callback.Run(Shell::CreateNewWindow(browser_context, params.url, nullptr, gfx::Size())->web_contents()); } #if defined(OS_POSIX) && !defined(OS_MACOSX) void ShellContentBrowserClient::GetAdditionalMappedFilesForChildProcess( const base::CommandLine& command_line, int child_process_id, FileDescriptorInfo* mappings) { #if defined(V8_USE_EXTERNAL_STARTUP_DATA) if (v8_natives_fd_.get() == -1 || v8_snapshot_fd_.get() == -1) { int v8_natives_fd = -1; int v8_snapshot_fd = -1; if (gin::V8Initializer::OpenV8FilesForChildProcesses(&v8_natives_fd, &v8_snapshot_fd)) { v8_natives_fd_.reset(v8_natives_fd); v8_snapshot_fd_.reset(v8_snapshot_fd); } } DCHECK(v8_natives_fd_.get() != -1 && v8_snapshot_fd_.get() != -1); mappings->Share(kV8NativesDataDescriptor, v8_natives_fd_.get()); mappings->Share(kV8SnapshotDataDescriptor, v8_snapshot_fd_.get()); #endif // V8_USE_EXTERNAL_STARTUP_DATA #if defined(OS_ANDROID) int flags = base::File::FLAG_OPEN | base::File::FLAG_READ; base::FilePath pak_file; bool r = PathService::Get(base::DIR_ANDROID_APP_DATA, &pak_file); CHECK(r); pak_file = pak_file.Append(FILE_PATH_LITERAL("paks")); pak_file = pak_file.Append(FILE_PATH_LITERAL("content_shell.pak")); base::File f(pak_file, flags); if (!f.IsValid()) { NOTREACHED() << "Failed to open file when creating renderer process: " << "content_shell.pak"; } mappings->Transfer(kShellPakDescriptor, base::ScopedFD(f.TakePlatformFile())); if (breakpad::IsCrashReporterEnabled()) { f = breakpad::CrashDumpManager::GetInstance()->CreateMinidumpFile( child_process_id); if (!f.IsValid()) { LOG(ERROR) << "Failed to create file for minidump, crash reporting will " << "be disabled for this process."; } else { mappings->Transfer(kAndroidMinidumpDescriptor, base::ScopedFD(f.TakePlatformFile())); } } #else // !defined(OS_ANDROID) int crash_signal_fd = GetCrashSignalFD(command_line); if (crash_signal_fd >= 0) { mappings->Share(kCrashDumpSignal, crash_signal_fd); } #endif // defined(OS_ANDROID) } #endif // defined(OS_POSIX) && !defined(OS_MACOSX) #if defined(OS_WIN) void ShellContentBrowserClient::PreSpawnRenderer(sandbox::TargetPolicy* policy, bool* success) { // Add sideloaded font files for testing. See also DIR_WINDOWS_FONTS // addition in |StartSandboxedProcess|. std::vector font_files = GetSideloadFontFiles(); for (std::vector::const_iterator i(font_files.begin()); i != font_files.end(); ++i) { policy->AddRule(sandbox::TargetPolicy::SUBSYS_FILES, sandbox::TargetPolicy::FILES_ALLOW_READONLY, base::UTF8ToWide(*i).c_str()); } } #endif // OS_WIN ShellBrowserContext* ShellContentBrowserClient::browser_context() { return shell_browser_main_parts_->browser_context(); } ShellBrowserContext* ShellContentBrowserClient::off_the_record_browser_context() { return shell_browser_main_parts_->off_the_record_browser_context(); } AccessTokenStore* ShellContentBrowserClient::CreateAccessTokenStore() { return new ShellAccessTokenStore(browser_context()); } ShellBrowserContext* ShellContentBrowserClient::ShellBrowserContextForBrowserContext( BrowserContext* content_browser_context) { if (content_browser_context == browser_context()) return browser_context(); DCHECK_EQ(content_browser_context, off_the_record_browser_context()); return off_the_record_browser_context(); } } // namespace content