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