| OLD | NEW |
| 1 // Copyright 2012 The Chromium Authors. All rights reserved. | 1 // Copyright 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "content/browser/child_process_launcher.h" | 5 #include "content/browser/child_process_launcher.h" |
| 6 | 6 |
| 7 #include <utility> | 7 #include <utility> |
| 8 | 8 |
| 9 #include "base/bind.h" | 9 #include "base/bind.h" |
| 10 #include "base/command_line.h" | 10 #include "base/command_line.h" |
| 11 #include "base/files/file_util.h" | 11 #include "base/files/file_util.h" |
| 12 #include "base/i18n/icu_util.h" | 12 #include "base/i18n/icu_util.h" |
| 13 #include "base/logging.h" | 13 #include "base/logging.h" |
| 14 #include "base/memory/scoped_ptr.h" | 14 #include "base/memory/scoped_ptr.h" |
| 15 #include "base/metrics/histogram.h" | 15 #include "base/metrics/histogram.h" |
| 16 #include "base/process/process.h" | 16 #include "base/process/process.h" |
| 17 #include "base/rand_util.h" |
| 17 #include "base/strings/string_number_conversions.h" | 18 #include "base/strings/string_number_conversions.h" |
| 19 #include "base/strings/stringprintf.h" |
| 20 #include "base/strings/utf_string_conversions.h" |
| 18 #include "base/synchronization/lock.h" | 21 #include "base/synchronization/lock.h" |
| 19 #include "base/threading/thread.h" | 22 #include "base/threading/thread.h" |
| 20 #include "build/build_config.h" | 23 #include "build/build_config.h" |
| 21 #include "content/public/browser/content_browser_client.h" | 24 #include "content/public/browser/content_browser_client.h" |
| 22 #include "content/public/common/content_descriptors.h" | 25 #include "content/public/common/content_descriptors.h" |
| 23 #include "content/public/common/content_switches.h" | 26 #include "content/public/common/content_switches.h" |
| 24 #include "content/public/common/result_codes.h" | 27 #include "content/public/common/result_codes.h" |
| 25 #include "content/public/common/sandboxed_process_launcher_delegate.h" | 28 #include "content/public/common/sandboxed_process_launcher_delegate.h" |
| 29 #include "mojo/edk/embedder/embedder.h" |
| 30 #include "mojo/edk/embedder/platform_channel_pair.h" |
| 31 #include "mojo/edk/embedder/scoped_platform_handle.h" |
| 26 | 32 |
| 27 #if defined(OS_WIN) | 33 #if defined(OS_WIN) |
| 28 #include "base/files/file_path.h" | 34 #include "base/files/file_path.h" |
| 29 #include "content/common/sandbox_win.h" | 35 #include "content/common/sandbox_win.h" |
| 30 #include "content/public/common/sandbox_init.h" | 36 #include "content/public/common/sandbox_init.h" |
| 31 #elif defined(OS_MACOSX) | 37 #elif defined(OS_MACOSX) |
| 32 #include "content/browser/bootstrap_sandbox_manager_mac.h" | 38 #include "content/browser/bootstrap_sandbox_manager_mac.h" |
| 33 #include "content/browser/mach_broker_mac.h" | 39 #include "content/browser/mach_broker_mac.h" |
| 34 #include "sandbox/mac/bootstrap_sandbox.h" | 40 #include "sandbox/mac/bootstrap_sandbox.h" |
| 35 #include "sandbox/mac/pre_exec_delegate.h" | 41 #include "sandbox/mac/pre_exec_delegate.h" |
| (...skipping 15 matching lines...) Expand all Loading... |
| 51 #include "gin/v8_initializer.h" | 57 #include "gin/v8_initializer.h" |
| 52 #endif | 58 #endif |
| 53 | 59 |
| 54 namespace content { | 60 namespace content { |
| 55 | 61 |
| 56 namespace { | 62 namespace { |
| 57 | 63 |
| 58 typedef base::Callback<void(ZygoteHandle, | 64 typedef base::Callback<void(ZygoteHandle, |
| 59 #if defined(OS_ANDROID) | 65 #if defined(OS_ANDROID) |
| 60 base::ScopedFD, | 66 base::ScopedFD, |
| 67 base::ScopedFD, |
| 61 #endif | 68 #endif |
| 62 base::Process)> NotifyCallback; | 69 base::Process)> NotifyCallback; |
| 63 | 70 |
| 64 void RecordHistogramsOnLauncherThread(base::TimeDelta launch_time) { | 71 void RecordHistogramsOnLauncherThread(base::TimeDelta launch_time) { |
| 65 DCHECK_CURRENTLY_ON(BrowserThread::PROCESS_LAUNCHER); | 72 DCHECK_CURRENTLY_ON(BrowserThread::PROCESS_LAUNCHER); |
| 66 // Log the launch time, separating out the first one (which will likely be | 73 // Log the launch time, separating out the first one (which will likely be |
| 67 // slower due to the rest of the browser initializing at the same time). | 74 // slower due to the rest of the browser initializing at the same time). |
| 68 static bool done_first_launch = false; | 75 static bool done_first_launch = false; |
| 69 if (done_first_launch) { | 76 if (done_first_launch) { |
| 70 UMA_HISTOGRAM_TIMES("MPArch.ChildProcessLaunchSubsequent", launch_time); | 77 UMA_HISTOGRAM_TIMES("MPArch.ChildProcessLaunchSubsequent", launch_time); |
| 71 } else { | 78 } else { |
| 72 UMA_HISTOGRAM_TIMES("MPArch.ChildProcessLaunchFirst", launch_time); | 79 UMA_HISTOGRAM_TIMES("MPArch.ChildProcessLaunchFirst", launch_time); |
| 73 done_first_launch = true; | 80 done_first_launch = true; |
| 74 } | 81 } |
| 75 } | 82 } |
| 76 | 83 |
| 84 #if defined(OS_WIN) |
| 85 // This generates a pipe name and secret to use for the Mojo IPC channel on |
| 86 // Windows. |
| 87 mojo::edk::ScopedPlatformHandle CreateMojoNamedPipe(base::CommandLine* cmd_line, |
| 88 std::string* secret) { |
| 89 std::string pipe_name = |
| 90 base::StringPrintf("\\\\.\\pipe\\chrome.%u.%u.%I64u", |
| 91 GetCurrentProcessId(), GetCurrentThreadId(), |
| 92 base::RandUint64()); |
| 93 std::string pipe_secret = mojo::edk::GenerateRandomToken(); |
| 94 |
| 95 const DWORD kOpenMode = PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED | |
| 96 FILE_FLAG_FIRST_PIPE_INSTANCE; |
| 97 const DWORD kPipeMode = PIPE_TYPE_BYTE | PIPE_READMODE_BYTE; |
| 98 mojo::edk::ScopedPlatformHandle pipe(mojo::edk::PlatformHandle( |
| 99 CreateNamedPipeW(base::UTF8ToWide(pipe_name).c_str(), kOpenMode, |
| 100 kPipeMode, |
| 101 1, // Max instances. |
| 102 4096, // Out buffer size. |
| 103 4096, // In buffer size. |
| 104 5000, // Timeout in milliseconds. |
| 105 nullptr))); // Default security descriptor. |
| 106 PCHECK(pipe.is_valid()); |
| 107 |
| 108 cmd_line->AppendSwitchASCII(switches::kMojoChannelName, pipe_name); |
| 109 cmd_line->AppendSwitchASCII(switches::kMojoChannelSecret, pipe_secret); |
| 110 |
| 111 *secret = pipe_secret; |
| 112 return pipe; |
| 113 } |
| 114 #endif |
| 115 |
| 77 #if defined(OS_ANDROID) | 116 #if defined(OS_ANDROID) |
| 78 // TODO(sievers): Remove this by defining better what happens on what | 117 // TODO(sievers): Remove this by defining better what happens on what |
| 79 // thread in the corresponding Java code. | 118 // thread in the corresponding Java code. |
| 80 void OnChildProcessStartedAndroid(const NotifyCallback& callback, | 119 void OnChildProcessStartedAndroid(const NotifyCallback& callback, |
| 81 BrowserThread::ID client_thread_id, | 120 BrowserThread::ID client_thread_id, |
| 82 const base::TimeTicks begin_launch_time, | 121 const base::TimeTicks begin_launch_time, |
| 83 base::ScopedFD ipcfd, | 122 base::ScopedFD ipcfd, |
| 123 base::ScopedFD mojo_fd, |
| 84 base::ProcessHandle handle) { | 124 base::ProcessHandle handle) { |
| 85 // This can be called on the launcher thread or UI thread. | 125 // This can be called on the launcher thread or UI thread. |
| 86 base::TimeDelta launch_time = base::TimeTicks::Now() - begin_launch_time; | 126 base::TimeDelta launch_time = base::TimeTicks::Now() - begin_launch_time; |
| 87 BrowserThread::PostTask( | 127 BrowserThread::PostTask( |
| 88 BrowserThread::PROCESS_LAUNCHER, FROM_HERE, | 128 BrowserThread::PROCESS_LAUNCHER, FROM_HERE, |
| 89 base::Bind(&RecordHistogramsOnLauncherThread, launch_time)); | 129 base::Bind(&RecordHistogramsOnLauncherThread, launch_time)); |
| 90 | 130 |
| 91 base::Closure callback_on_client_thread( | 131 base::Closure callback_on_client_thread( |
| 92 base::Bind(callback, nullptr, base::Passed(&ipcfd), | 132 base::Bind(callback, nullptr, base::Passed(&ipcfd), |
| 93 base::Passed(base::Process(handle)))); | 133 base::Passed(&mojo_fd), base::Passed(base::Process(handle)))); |
| 94 if (BrowserThread::CurrentlyOn(client_thread_id)) { | 134 if (BrowserThread::CurrentlyOn(client_thread_id)) { |
| 95 callback_on_client_thread.Run(); | 135 callback_on_client_thread.Run(); |
| 96 } else { | 136 } else { |
| 97 BrowserThread::PostTask( | 137 BrowserThread::PostTask( |
| 98 client_thread_id, FROM_HERE, callback_on_client_thread); | 138 client_thread_id, FROM_HERE, callback_on_client_thread); |
| 99 } | 139 } |
| 100 } | 140 } |
| 101 #endif | 141 #endif |
| 102 | 142 |
| 103 void LaunchOnLauncherThread(const NotifyCallback& callback, | 143 void LaunchOnLauncherThread(const NotifyCallback& callback, |
| 104 BrowserThread::ID client_thread_id, | 144 BrowserThread::ID client_thread_id, |
| 105 int child_process_id, | 145 int child_process_id, |
| 106 SandboxedProcessLauncherDelegate* delegate, | 146 SandboxedProcessLauncherDelegate* delegate, |
| 107 #if defined(OS_ANDROID) | 147 #if defined(OS_ANDROID) |
| 108 base::ScopedFD ipcfd, | 148 base::ScopedFD ipcfd, |
| 109 #endif | 149 #endif |
| 150 mojo::edk::ScopedPlatformHandle client_handle, |
| 110 base::CommandLine* cmd_line) { | 151 base::CommandLine* cmd_line) { |
| 111 DCHECK_CURRENTLY_ON(BrowserThread::PROCESS_LAUNCHER); | 152 DCHECK_CURRENTLY_ON(BrowserThread::PROCESS_LAUNCHER); |
| 112 scoped_ptr<SandboxedProcessLauncherDelegate> delegate_deleter(delegate); | 153 scoped_ptr<SandboxedProcessLauncherDelegate> delegate_deleter(delegate); |
| 113 #if !defined(OS_ANDROID) | 154 #if !defined(OS_ANDROID) |
| 114 ZygoteHandle zygote = nullptr; | 155 ZygoteHandle zygote = nullptr; |
| 115 #endif | 156 #endif |
| 116 #if defined(OS_WIN) | 157 #if defined(OS_WIN) |
| 117 bool launch_elevated = delegate->ShouldLaunchElevated(); | 158 bool launch_elevated = delegate->ShouldLaunchElevated(); |
| 118 #elif defined(OS_MACOSX) | 159 #elif defined(OS_MACOSX) |
| 119 base::EnvironmentMap env = delegate->GetEnvironment(); | 160 base::EnvironmentMap env = delegate->GetEnvironment(); |
| (...skipping 13 matching lines...) Expand all Loading... |
| 133 process = base::LaunchElevatedProcess(*cmd_line, options); | 174 process = base::LaunchElevatedProcess(*cmd_line, options); |
| 134 } else { | 175 } else { |
| 135 process = StartSandboxedProcess(delegate, cmd_line); | 176 process = StartSandboxedProcess(delegate, cmd_line); |
| 136 } | 177 } |
| 137 #elif defined(OS_POSIX) | 178 #elif defined(OS_POSIX) |
| 138 std::string process_type = | 179 std::string process_type = |
| 139 cmd_line->GetSwitchValueASCII(switches::kProcessType); | 180 cmd_line->GetSwitchValueASCII(switches::kProcessType); |
| 140 scoped_ptr<FileDescriptorInfo> files_to_register( | 181 scoped_ptr<FileDescriptorInfo> files_to_register( |
| 141 FileDescriptorInfoImpl::Create()); | 182 FileDescriptorInfoImpl::Create()); |
| 142 | 183 |
| 184 base::ScopedFD mojo_fd(client_handle.release().handle); |
| 185 DCHECK(mojo_fd.is_valid()); |
| 186 |
| 143 #if defined(OS_ANDROID) | 187 #if defined(OS_ANDROID) |
| 144 files_to_register->Share(kPrimaryIPCChannel, ipcfd.get()); | 188 files_to_register->Share(kPrimaryIPCChannel, ipcfd.get()); |
| 189 files_to_register->Share(kMojoIPCChannel, mojo_fd.get()); |
| 145 #else | 190 #else |
| 146 files_to_register->Transfer(kPrimaryIPCChannel, std::move(ipcfd)); | 191 files_to_register->Transfer(kPrimaryIPCChannel, std::move(ipcfd)); |
| 192 files_to_register->Transfer(kMojoIPCChannel, std::move(mojo_fd)); |
| 147 #endif | 193 #endif |
| 148 #endif | 194 #endif |
| 149 | 195 |
| 150 #if defined(OS_POSIX) && !defined(OS_MACOSX) | 196 #if defined(OS_POSIX) && !defined(OS_MACOSX) |
| 151 std::map<int, base::MemoryMappedFile::Region> regions; | 197 std::map<int, base::MemoryMappedFile::Region> regions; |
| 152 GetContentClient()->browser()->GetAdditionalMappedFilesForChildProcess( | 198 GetContentClient()->browser()->GetAdditionalMappedFilesForChildProcess( |
| 153 *cmd_line, child_process_id, files_to_register.get() | 199 *cmd_line, child_process_id, files_to_register.get() |
| 154 #if defined(OS_ANDROID) | 200 #if defined(OS_ANDROID) |
| 155 , ®ions | 201 , ®ions |
| 156 #endif | 202 #endif |
| (...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 214 kAndroidICUDataDescriptor, | 260 kAndroidICUDataDescriptor, |
| 215 base::i18n::GetIcuDataFileHandle(®ions[kAndroidICUDataDescriptor])); | 261 base::i18n::GetIcuDataFileHandle(®ions[kAndroidICUDataDescriptor])); |
| 216 | 262 |
| 217 // Android WebView runs in single process, ensure that we never get here | 263 // Android WebView runs in single process, ensure that we never get here |
| 218 // when running in single process mode. | 264 // when running in single process mode. |
| 219 CHECK(!cmd_line->HasSwitch(switches::kSingleProcess)); | 265 CHECK(!cmd_line->HasSwitch(switches::kSingleProcess)); |
| 220 | 266 |
| 221 StartChildProcess( | 267 StartChildProcess( |
| 222 cmd_line->argv(), child_process_id, std::move(files_to_register), regions, | 268 cmd_line->argv(), child_process_id, std::move(files_to_register), regions, |
| 223 base::Bind(&OnChildProcessStartedAndroid, callback, client_thread_id, | 269 base::Bind(&OnChildProcessStartedAndroid, callback, client_thread_id, |
| 224 begin_launch_time, base::Passed(&ipcfd))); | 270 begin_launch_time, base::Passed(&ipcfd), |
| 271 base::Passed(&mojo_fd))); |
| 225 | 272 |
| 226 #elif defined(OS_POSIX) | 273 #elif defined(OS_POSIX) |
| 227 // We need to close the client end of the IPC channel to reliably detect | 274 // We need to close the client end of the IPC channel to reliably detect |
| 228 // child termination. | 275 // child termination. |
| 229 | 276 |
| 230 #if !defined(OS_MACOSX) | 277 #if !defined(OS_MACOSX) |
| 231 ZygoteHandle* zygote_handle = delegate->GetZygote(); | 278 ZygoteHandle* zygote_handle = delegate->GetZygote(); |
| 232 // If |zygote_handle| is null, a zygote should not be used. | 279 // If |zygote_handle| is null, a zygote should not be used. |
| 233 if (zygote_handle) { | 280 if (zygote_handle) { |
| 234 // This code runs on the PROCESS_LAUNCHER thread so race conditions are not | 281 // This code runs on the PROCESS_LAUNCHER thread so race conditions are not |
| (...skipping 179 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 414 !cmd_line->HasSwitch(switches::kNoSandbox)); | 461 !cmd_line->HasSwitch(switches::kNoSandbox)); |
| 415 | 462 |
| 416 // We need to close the client end of the IPC channel to reliably detect | 463 // We need to close the client end of the IPC channel to reliably detect |
| 417 // child termination. We will close this fd after we create the child | 464 // child termination. We will close this fd after we create the child |
| 418 // process which is asynchronous on Android. | 465 // process which is asynchronous on Android. |
| 419 base::ScopedFD ipcfd(delegate->TakeIpcFd().release()); | 466 base::ScopedFD ipcfd(delegate->TakeIpcFd().release()); |
| 420 #endif | 467 #endif |
| 421 NotifyCallback reply_callback(base::Bind(&ChildProcessLauncher::DidLaunch, | 468 NotifyCallback reply_callback(base::Bind(&ChildProcessLauncher::DidLaunch, |
| 422 weak_factory_.GetWeakPtr(), | 469 weak_factory_.GetWeakPtr(), |
| 423 terminate_child_on_shutdown_)); | 470 terminate_child_on_shutdown_)); |
| 471 mojo::edk::ScopedPlatformHandle client_handle; |
| 472 #if defined(OS_WIN) |
| 473 mojo_server_handle_ = CreateMojoNamedPipe(cmd_line, &mojo_secret_); |
| 474 #else |
| 475 mojo::edk::PlatformChannelPair platform_channel; |
| 476 client_handle = platform_channel.PassClientHandle(); |
| 477 mojo_server_handle_ = platform_channel.PassServerHandle(); |
| 478 #endif |
| 424 BrowserThread::PostTask( | 479 BrowserThread::PostTask( |
| 425 BrowserThread::PROCESS_LAUNCHER, FROM_HERE, | 480 BrowserThread::PROCESS_LAUNCHER, FROM_HERE, |
| 426 base::Bind(&LaunchOnLauncherThread, reply_callback, client_thread_id_, | 481 base::Bind(&LaunchOnLauncherThread, reply_callback, client_thread_id_, |
| 427 child_process_id, delegate, | 482 child_process_id, delegate, |
| 428 #if defined(OS_ANDROID) | 483 #if defined(OS_ANDROID) |
| 429 base::Passed(&ipcfd), | 484 base::Passed(&ipcfd), |
| 430 #endif | 485 #endif |
| 431 cmd_line)); | 486 base::Passed(&client_handle), cmd_line)); |
| 432 } | 487 } |
| 433 | 488 |
| 434 void ChildProcessLauncher::UpdateTerminationStatus(bool known_dead) { | 489 void ChildProcessLauncher::UpdateTerminationStatus(bool known_dead) { |
| 435 DCHECK(CalledOnValidThread()); | 490 DCHECK(CalledOnValidThread()); |
| 436 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID) | 491 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID) |
| 437 if (zygote_) { | 492 if (zygote_) { |
| 438 termination_status_ = zygote_->GetTerminationStatus( | 493 termination_status_ = zygote_->GetTerminationStatus( |
| 439 process_.Handle(), known_dead, &exit_code_); | 494 process_.Handle(), known_dead, &exit_code_); |
| 440 } else if (known_dead) { | 495 } else if (known_dead) { |
| 441 termination_status_ = | 496 termination_status_ = |
| (...skipping 23 matching lines...) Expand all Loading... |
| 465 base::Bind(&SetProcessBackgroundedOnLauncherThread, | 520 base::Bind(&SetProcessBackgroundedOnLauncherThread, |
| 466 base::Passed(&to_pass), background)); | 521 base::Passed(&to_pass), background)); |
| 467 } | 522 } |
| 468 | 523 |
| 469 void ChildProcessLauncher::DidLaunch( | 524 void ChildProcessLauncher::DidLaunch( |
| 470 base::WeakPtr<ChildProcessLauncher> instance, | 525 base::WeakPtr<ChildProcessLauncher> instance, |
| 471 bool terminate_on_shutdown, | 526 bool terminate_on_shutdown, |
| 472 ZygoteHandle zygote, | 527 ZygoteHandle zygote, |
| 473 #if defined(OS_ANDROID) | 528 #if defined(OS_ANDROID) |
| 474 base::ScopedFD ipcfd, | 529 base::ScopedFD ipcfd, |
| 530 base::ScopedFD mojo_fd, |
| 475 #endif | 531 #endif |
| 476 base::Process process) { | 532 base::Process process) { |
| 477 if (!process.IsValid()) | 533 if (!process.IsValid()) |
| 478 LOG(ERROR) << "Failed to launch child process"; | 534 LOG(ERROR) << "Failed to launch child process"; |
| 479 | 535 |
| 480 if (instance.get()) { | 536 if (instance.get()) { |
| 481 instance->Notify(zygote, | 537 instance->Notify(zygote, |
| 482 #if defined(OS_ANDROID) | 538 #if defined(OS_ANDROID) |
| 483 std::move(ipcfd), | 539 std::move(ipcfd), |
| 484 #endif | 540 #endif |
| (...skipping 11 matching lines...) Expand all Loading... |
| 496 | 552 |
| 497 void ChildProcessLauncher::Notify(ZygoteHandle zygote, | 553 void ChildProcessLauncher::Notify(ZygoteHandle zygote, |
| 498 #if defined(OS_ANDROID) | 554 #if defined(OS_ANDROID) |
| 499 base::ScopedFD ipcfd, | 555 base::ScopedFD ipcfd, |
| 500 #endif | 556 #endif |
| 501 base::Process process) { | 557 base::Process process) { |
| 502 DCHECK(CalledOnValidThread()); | 558 DCHECK(CalledOnValidThread()); |
| 503 starting_ = false; | 559 starting_ = false; |
| 504 process_ = std::move(process); | 560 process_ = std::move(process); |
| 505 | 561 |
| 562 if (process_.IsValid()) { |
| 563 // Set up Mojo IPC to the new process. |
| 564 #if defined(OS_WIN) |
| 565 mojo::edk::ChildProcessLaunched(process_.Handle(), |
| 566 std::move(mojo_server_handle_), |
| 567 mojo_secret_); |
| 568 #else |
| 569 mojo::edk::ChildProcessLaunched(process_.Handle(), |
| 570 std::move(mojo_server_handle_)); |
| 571 #endif // defined(OS_WIN) |
| 572 } |
| 573 |
| 506 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID) | 574 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID) |
| 507 zygote_ = zygote; | 575 zygote_ = zygote; |
| 508 #endif | 576 #endif |
| 509 if (process_.IsValid()) { | 577 if (process_.IsValid()) { |
| 510 client_->OnProcessLaunched(); | 578 client_->OnProcessLaunched(); |
| 511 } else { | 579 } else { |
| 512 termination_status_ = base::TERMINATION_STATUS_LAUNCH_FAILED; | 580 termination_status_ = base::TERMINATION_STATUS_LAUNCH_FAILED; |
| 513 client_->OnProcessLaunchFailed(); | 581 client_->OnProcessLaunchFailed(); |
| 514 } | 582 } |
| 515 } | 583 } |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 554 } | 622 } |
| 555 | 623 |
| 556 ChildProcessLauncher::Client* ChildProcessLauncher::ReplaceClientForTest( | 624 ChildProcessLauncher::Client* ChildProcessLauncher::ReplaceClientForTest( |
| 557 Client* client) { | 625 Client* client) { |
| 558 Client* ret = client_; | 626 Client* ret = client_; |
| 559 client_ = client; | 627 client_ = client; |
| 560 return ret; | 628 return ret; |
| 561 } | 629 } |
| 562 | 630 |
| 563 } // namespace content | 631 } // namespace content |
| OLD | NEW |