| 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/launch.h" | 16 #include "base/process/launch.h" |
| 17 #include "base/process/process.h" | 17 #include "base/process/process.h" |
| 18 #include "base/strings/string_number_conversions.h" | 18 #include "base/strings/string_number_conversions.h" |
| 19 #include "base/synchronization/lock.h" | 19 #include "base/synchronization/lock.h" |
| 20 #include "base/threading/thread.h" | 20 #include "base/threading/thread.h" |
| 21 #include "build/build_config.h" | 21 #include "build/build_config.h" |
| 22 #include "content/public/browser/content_browser_client.h" | 22 #include "content/public/browser/content_browser_client.h" |
| 23 #include "content/public/common/content_descriptors.h" | 23 #include "content/public/common/content_descriptors.h" |
| 24 #include "content/public/common/content_switches.h" | 24 #include "content/public/common/content_switches.h" |
| 25 #include "content/public/common/result_codes.h" | 25 #include "content/public/common/result_codes.h" |
| 26 #include "content/public/common/sandboxed_process_launcher_delegate.h" | 26 #include "content/public/common/sandboxed_process_launcher_delegate.h" |
| 27 #include "mojo/edk/embedder/embedder.h" |
| 28 #include "mojo/edk/embedder/scoped_platform_handle.h" |
| 27 | 29 |
| 28 #if defined(OS_WIN) | 30 #if defined(OS_WIN) |
| 29 #include "base/files/file_path.h" | 31 #include "base/files/file_path.h" |
| 32 #include "base/win/scoped_handle.h" |
| 33 #include "base/win/win_util.h" |
| 30 #include "content/common/sandbox_win.h" | 34 #include "content/common/sandbox_win.h" |
| 31 #include "content/public/common/sandbox_init.h" | 35 #include "content/public/common/sandbox_init.h" |
| 32 #elif defined(OS_MACOSX) | 36 #elif defined(OS_MACOSX) |
| 33 #include "content/browser/bootstrap_sandbox_manager_mac.h" | 37 #include "content/browser/bootstrap_sandbox_manager_mac.h" |
| 34 #include "content/browser/mach_broker_mac.h" | 38 #include "content/browser/mach_broker_mac.h" |
| 35 #include "sandbox/mac/bootstrap_sandbox.h" | 39 #include "sandbox/mac/bootstrap_sandbox.h" |
| 36 #include "sandbox/mac/pre_exec_delegate.h" | 40 #include "sandbox/mac/pre_exec_delegate.h" |
| 37 #elif defined(OS_ANDROID) | 41 #elif defined(OS_ANDROID) |
| 38 #include "base/android/jni_android.h" | 42 #include "base/android/jni_android.h" |
| 39 #include "content/browser/android/child_process_launcher_android.h" | 43 #include "content/browser/android/child_process_launcher_android.h" |
| (...skipping 12 matching lines...) Expand all Loading... |
| 52 #include "gin/v8_initializer.h" | 56 #include "gin/v8_initializer.h" |
| 53 #endif | 57 #endif |
| 54 | 58 |
| 55 namespace content { | 59 namespace content { |
| 56 | 60 |
| 57 namespace { | 61 namespace { |
| 58 | 62 |
| 59 typedef base::Callback<void(ZygoteHandle, | 63 typedef base::Callback<void(ZygoteHandle, |
| 60 #if defined(OS_ANDROID) | 64 #if defined(OS_ANDROID) |
| 61 base::ScopedFD, | 65 base::ScopedFD, |
| 66 base::ScopedFD, |
| 62 #endif | 67 #endif |
| 63 base::Process)> NotifyCallback; | 68 base::Process)> NotifyCallback; |
| 64 | 69 |
| 65 void RecordHistogramsOnLauncherThread(base::TimeDelta launch_time) { | 70 void RecordHistogramsOnLauncherThread(base::TimeDelta launch_time) { |
| 66 DCHECK_CURRENTLY_ON(BrowserThread::PROCESS_LAUNCHER); | 71 DCHECK_CURRENTLY_ON(BrowserThread::PROCESS_LAUNCHER); |
| 67 // Log the launch time, separating out the first one (which will likely be | 72 // Log the launch time, separating out the first one (which will likely be |
| 68 // slower due to the rest of the browser initializing at the same time). | 73 // slower due to the rest of the browser initializing at the same time). |
| 69 static bool done_first_launch = false; | 74 static bool done_first_launch = false; |
| 70 if (done_first_launch) { | 75 if (done_first_launch) { |
| 71 UMA_HISTOGRAM_TIMES("MPArch.ChildProcessLaunchSubsequent", launch_time); | 76 UMA_HISTOGRAM_TIMES("MPArch.ChildProcessLaunchSubsequent", launch_time); |
| 72 } else { | 77 } else { |
| 73 UMA_HISTOGRAM_TIMES("MPArch.ChildProcessLaunchFirst", launch_time); | 78 UMA_HISTOGRAM_TIMES("MPArch.ChildProcessLaunchFirst", launch_time); |
| 74 done_first_launch = true; | 79 done_first_launch = true; |
| 75 } | 80 } |
| 76 } | 81 } |
| 77 | 82 |
| 78 #if defined(OS_ANDROID) | 83 #if defined(OS_ANDROID) |
| 79 // TODO(sievers): Remove this by defining better what happens on what | 84 // TODO(sievers): Remove this by defining better what happens on what |
| 80 // thread in the corresponding Java code. | 85 // thread in the corresponding Java code. |
| 81 void OnChildProcessStartedAndroid(const NotifyCallback& callback, | 86 void OnChildProcessStartedAndroid(const NotifyCallback& callback, |
| 82 BrowserThread::ID client_thread_id, | 87 BrowserThread::ID client_thread_id, |
| 83 const base::TimeTicks begin_launch_time, | 88 const base::TimeTicks begin_launch_time, |
| 84 base::ScopedFD ipcfd, | 89 base::ScopedFD ipcfd, |
| 90 base::ScopedFD mojo_fd, |
| 85 base::ProcessHandle handle) { | 91 base::ProcessHandle handle) { |
| 86 // This can be called on the launcher thread or UI thread. | 92 // This can be called on the launcher thread or UI thread. |
| 87 base::TimeDelta launch_time = base::TimeTicks::Now() - begin_launch_time; | 93 base::TimeDelta launch_time = base::TimeTicks::Now() - begin_launch_time; |
| 88 BrowserThread::PostTask( | 94 BrowserThread::PostTask( |
| 89 BrowserThread::PROCESS_LAUNCHER, FROM_HERE, | 95 BrowserThread::PROCESS_LAUNCHER, FROM_HERE, |
| 90 base::Bind(&RecordHistogramsOnLauncherThread, launch_time)); | 96 base::Bind(&RecordHistogramsOnLauncherThread, launch_time)); |
| 91 | 97 |
| 92 base::Closure callback_on_client_thread( | 98 base::Closure callback_on_client_thread( |
| 93 base::Bind(callback, nullptr, base::Passed(&ipcfd), | 99 base::Bind(callback, nullptr, base::Passed(&ipcfd), |
| 94 base::Passed(base::Process(handle)))); | 100 base::Passed(&mojo_fd), base::Passed(base::Process(handle)))); |
| 95 if (BrowserThread::CurrentlyOn(client_thread_id)) { | 101 if (BrowserThread::CurrentlyOn(client_thread_id)) { |
| 96 callback_on_client_thread.Run(); | 102 callback_on_client_thread.Run(); |
| 97 } else { | 103 } else { |
| 98 BrowserThread::PostTask( | 104 BrowserThread::PostTask( |
| 99 client_thread_id, FROM_HERE, callback_on_client_thread); | 105 client_thread_id, FROM_HERE, callback_on_client_thread); |
| 100 } | 106 } |
| 101 } | 107 } |
| 102 #endif | 108 #endif |
| 103 | 109 |
| 104 void LaunchOnLauncherThread(const NotifyCallback& callback, | 110 void LaunchOnLauncherThread(const NotifyCallback& callback, |
| 105 BrowserThread::ID client_thread_id, | 111 BrowserThread::ID client_thread_id, |
| 106 int child_process_id, | 112 int child_process_id, |
| 107 SandboxedProcessLauncherDelegate* delegate, | 113 SandboxedProcessLauncherDelegate* delegate, |
| 108 #if defined(OS_ANDROID) | 114 #if defined(OS_ANDROID) |
| 109 base::ScopedFD ipcfd, | 115 base::ScopedFD ipcfd, |
| 110 #endif | 116 #endif |
| 117 mojo::edk::ScopedPlatformHandle client_handle, |
| 111 base::CommandLine* cmd_line) { | 118 base::CommandLine* cmd_line) { |
| 112 DCHECK_CURRENTLY_ON(BrowserThread::PROCESS_LAUNCHER); | 119 DCHECK_CURRENTLY_ON(BrowserThread::PROCESS_LAUNCHER); |
| 113 scoped_ptr<SandboxedProcessLauncherDelegate> delegate_deleter(delegate); | 120 scoped_ptr<SandboxedProcessLauncherDelegate> delegate_deleter(delegate); |
| 114 #if !defined(OS_ANDROID) | 121 #if !defined(OS_ANDROID) |
| 115 ZygoteHandle zygote = nullptr; | 122 ZygoteHandle zygote = nullptr; |
| 116 #endif | 123 #endif |
| 117 #if defined(OS_WIN) | 124 #if defined(OS_WIN) |
| 118 bool launch_elevated = delegate->ShouldLaunchElevated(); | 125 bool launch_elevated = delegate->ShouldLaunchElevated(); |
| 119 #elif defined(OS_MACOSX) | 126 #elif defined(OS_MACOSX) |
| 120 base::EnvironmentMap env = delegate->GetEnvironment(); | 127 base::EnvironmentMap env = delegate->GetEnvironment(); |
| 121 base::ScopedFD ipcfd = delegate->TakeIpcFd(); | 128 base::ScopedFD ipcfd = delegate->TakeIpcFd(); |
| 122 #elif defined(OS_POSIX) && !defined(OS_ANDROID) | 129 #elif defined(OS_POSIX) && !defined(OS_ANDROID) |
| 123 base::EnvironmentMap env = delegate->GetEnvironment(); | 130 base::EnvironmentMap env = delegate->GetEnvironment(); |
| 124 base::ScopedFD ipcfd = delegate->TakeIpcFd(); | 131 base::ScopedFD ipcfd = delegate->TakeIpcFd(); |
| 125 #endif | 132 #endif |
| 126 scoped_ptr<base::CommandLine> cmd_line_deleter(cmd_line); | 133 scoped_ptr<base::CommandLine> cmd_line_deleter(cmd_line); |
| 127 base::TimeTicks begin_launch_time = base::TimeTicks::Now(); | 134 base::TimeTicks begin_launch_time = base::TimeTicks::Now(); |
| 128 | 135 |
| 129 base::Process process; | 136 base::Process process; |
| 130 #if defined(OS_WIN) | 137 #if defined(OS_WIN) |
| 131 if (launch_elevated) { | 138 if (launch_elevated) { |
| 139 // TODO(rockot): We may want to support Mojo IPC to elevated processes as |
| 140 // well, but this isn't currently feasible without sharing a pipe path on |
| 141 // the command line as elevated process launch goes through ShellExecuteEx. |
| 132 base::LaunchOptions options; | 142 base::LaunchOptions options; |
| 133 options.start_hidden = true; | 143 options.start_hidden = true; |
| 134 process = base::LaunchElevatedProcess(*cmd_line, options); | 144 process = base::LaunchElevatedProcess(*cmd_line, options); |
| 135 } else { | 145 } else { |
| 136 process = StartSandboxedProcess( | 146 base::HandlesToInheritVector handles; |
| 137 delegate, cmd_line, base::HandlesToInheritVector()); | 147 handles.push_back(client_handle.get().handle); |
| 148 cmd_line->AppendSwitchASCII( |
| 149 mojo::edk::PlatformChannelPair::kMojoPlatformChannelHandleSwitch, |
| 150 base::UintToString(base::win::HandleToUint32(handles[0]))); |
| 151 process = StartSandboxedProcess(delegate, cmd_line, handles); |
| 138 } | 152 } |
| 139 #elif defined(OS_POSIX) | 153 #elif defined(OS_POSIX) |
| 140 std::string process_type = | 154 std::string process_type = |
| 141 cmd_line->GetSwitchValueASCII(switches::kProcessType); | 155 cmd_line->GetSwitchValueASCII(switches::kProcessType); |
| 142 scoped_ptr<FileDescriptorInfo> files_to_register( | 156 scoped_ptr<FileDescriptorInfo> files_to_register( |
| 143 FileDescriptorInfoImpl::Create()); | 157 FileDescriptorInfoImpl::Create()); |
| 144 | 158 |
| 159 base::ScopedFD mojo_fd(client_handle.release().handle); |
| 160 DCHECK(mojo_fd.is_valid()); |
| 161 |
| 145 #if defined(OS_ANDROID) | 162 #if defined(OS_ANDROID) |
| 146 files_to_register->Share(kPrimaryIPCChannel, ipcfd.get()); | 163 files_to_register->Share(kPrimaryIPCChannel, ipcfd.get()); |
| 164 files_to_register->Share(kMojoIPCChannel, mojo_fd.get()); |
| 147 #else | 165 #else |
| 148 files_to_register->Transfer(kPrimaryIPCChannel, std::move(ipcfd)); | 166 files_to_register->Transfer(kPrimaryIPCChannel, std::move(ipcfd)); |
| 167 files_to_register->Transfer(kMojoIPCChannel, std::move(mojo_fd)); |
| 149 #endif | 168 #endif |
| 150 #endif | 169 #endif |
| 151 | 170 |
| 152 #if defined(OS_POSIX) && !defined(OS_MACOSX) | 171 #if defined(OS_POSIX) && !defined(OS_MACOSX) |
| 153 std::map<int, base::MemoryMappedFile::Region> regions; | 172 std::map<int, base::MemoryMappedFile::Region> regions; |
| 154 GetContentClient()->browser()->GetAdditionalMappedFilesForChildProcess( | 173 GetContentClient()->browser()->GetAdditionalMappedFilesForChildProcess( |
| 155 *cmd_line, child_process_id, files_to_register.get() | 174 *cmd_line, child_process_id, files_to_register.get() |
| 156 #if defined(OS_ANDROID) | 175 #if defined(OS_ANDROID) |
| 157 , ®ions | 176 , ®ions |
| 158 #endif | 177 #endif |
| (...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 216 kAndroidICUDataDescriptor, | 235 kAndroidICUDataDescriptor, |
| 217 base::i18n::GetIcuDataFileHandle(®ions[kAndroidICUDataDescriptor])); | 236 base::i18n::GetIcuDataFileHandle(®ions[kAndroidICUDataDescriptor])); |
| 218 | 237 |
| 219 // Android WebView runs in single process, ensure that we never get here | 238 // Android WebView runs in single process, ensure that we never get here |
| 220 // when running in single process mode. | 239 // when running in single process mode. |
| 221 CHECK(!cmd_line->HasSwitch(switches::kSingleProcess)); | 240 CHECK(!cmd_line->HasSwitch(switches::kSingleProcess)); |
| 222 | 241 |
| 223 StartChildProcess( | 242 StartChildProcess( |
| 224 cmd_line->argv(), child_process_id, std::move(files_to_register), regions, | 243 cmd_line->argv(), child_process_id, std::move(files_to_register), regions, |
| 225 base::Bind(&OnChildProcessStartedAndroid, callback, client_thread_id, | 244 base::Bind(&OnChildProcessStartedAndroid, callback, client_thread_id, |
| 226 begin_launch_time, base::Passed(&ipcfd))); | 245 begin_launch_time, base::Passed(&ipcfd), |
| 246 base::Passed(&mojo_fd))); |
| 227 | 247 |
| 228 #elif defined(OS_POSIX) | 248 #elif defined(OS_POSIX) |
| 229 // We need to close the client end of the IPC channel to reliably detect | 249 // We need to close the client end of the IPC channel to reliably detect |
| 230 // child termination. | 250 // child termination. |
| 231 | 251 |
| 232 #if !defined(OS_MACOSX) | 252 #if !defined(OS_MACOSX) |
| 233 ZygoteHandle* zygote_handle = delegate->GetZygote(); | 253 ZygoteHandle* zygote_handle = delegate->GetZygote(); |
| 234 // If |zygote_handle| is null, a zygote should not be used. | 254 // If |zygote_handle| is null, a zygote should not be used. |
| 235 if (zygote_handle) { | 255 if (zygote_handle) { |
| 236 // This code runs on the PROCESS_LAUNCHER thread so race conditions are not | 256 // 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... |
| 416 !cmd_line->HasSwitch(switches::kNoSandbox)); | 436 !cmd_line->HasSwitch(switches::kNoSandbox)); |
| 417 | 437 |
| 418 // We need to close the client end of the IPC channel to reliably detect | 438 // We need to close the client end of the IPC channel to reliably detect |
| 419 // child termination. We will close this fd after we create the child | 439 // child termination. We will close this fd after we create the child |
| 420 // process which is asynchronous on Android. | 440 // process which is asynchronous on Android. |
| 421 base::ScopedFD ipcfd(delegate->TakeIpcFd().release()); | 441 base::ScopedFD ipcfd(delegate->TakeIpcFd().release()); |
| 422 #endif | 442 #endif |
| 423 NotifyCallback reply_callback(base::Bind(&ChildProcessLauncher::DidLaunch, | 443 NotifyCallback reply_callback(base::Bind(&ChildProcessLauncher::DidLaunch, |
| 424 weak_factory_.GetWeakPtr(), | 444 weak_factory_.GetWeakPtr(), |
| 425 terminate_child_on_shutdown_)); | 445 terminate_child_on_shutdown_)); |
| 446 mojo::edk::ScopedPlatformHandle client_handle = |
| 447 mojo_platform_channel_.PassClientHandle(); |
| 426 BrowserThread::PostTask( | 448 BrowserThread::PostTask( |
| 427 BrowserThread::PROCESS_LAUNCHER, FROM_HERE, | 449 BrowserThread::PROCESS_LAUNCHER, FROM_HERE, |
| 428 base::Bind(&LaunchOnLauncherThread, reply_callback, client_thread_id_, | 450 base::Bind(&LaunchOnLauncherThread, reply_callback, client_thread_id_, |
| 429 child_process_id, delegate, | 451 child_process_id, delegate, |
| 430 #if defined(OS_ANDROID) | 452 #if defined(OS_ANDROID) |
| 431 base::Passed(&ipcfd), | 453 base::Passed(&ipcfd), |
| 432 #endif | 454 #endif |
| 433 cmd_line)); | 455 base::Passed(&client_handle), cmd_line)); |
| 434 } | 456 } |
| 435 | 457 |
| 436 void ChildProcessLauncher::UpdateTerminationStatus(bool known_dead) { | 458 void ChildProcessLauncher::UpdateTerminationStatus(bool known_dead) { |
| 437 DCHECK(CalledOnValidThread()); | 459 DCHECK(CalledOnValidThread()); |
| 438 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID) | 460 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID) |
| 439 if (zygote_) { | 461 if (zygote_) { |
| 440 termination_status_ = zygote_->GetTerminationStatus( | 462 termination_status_ = zygote_->GetTerminationStatus( |
| 441 process_.Handle(), known_dead, &exit_code_); | 463 process_.Handle(), known_dead, &exit_code_); |
| 442 } else if (known_dead) { | 464 } else if (known_dead) { |
| 443 termination_status_ = | 465 termination_status_ = |
| (...skipping 23 matching lines...) Expand all Loading... |
| 467 base::Bind(&SetProcessBackgroundedOnLauncherThread, | 489 base::Bind(&SetProcessBackgroundedOnLauncherThread, |
| 468 base::Passed(&to_pass), background)); | 490 base::Passed(&to_pass), background)); |
| 469 } | 491 } |
| 470 | 492 |
| 471 void ChildProcessLauncher::DidLaunch( | 493 void ChildProcessLauncher::DidLaunch( |
| 472 base::WeakPtr<ChildProcessLauncher> instance, | 494 base::WeakPtr<ChildProcessLauncher> instance, |
| 473 bool terminate_on_shutdown, | 495 bool terminate_on_shutdown, |
| 474 ZygoteHandle zygote, | 496 ZygoteHandle zygote, |
| 475 #if defined(OS_ANDROID) | 497 #if defined(OS_ANDROID) |
| 476 base::ScopedFD ipcfd, | 498 base::ScopedFD ipcfd, |
| 499 base::ScopedFD mojo_fd, |
| 477 #endif | 500 #endif |
| 478 base::Process process) { | 501 base::Process process) { |
| 479 if (!process.IsValid()) | 502 if (!process.IsValid()) |
| 480 LOG(ERROR) << "Failed to launch child process"; | 503 LOG(ERROR) << "Failed to launch child process"; |
| 481 | 504 |
| 482 if (instance.get()) { | 505 if (instance.get()) { |
| 483 instance->Notify(zygote, | 506 instance->Notify(zygote, |
| 484 #if defined(OS_ANDROID) | 507 #if defined(OS_ANDROID) |
| 485 std::move(ipcfd), | 508 std::move(ipcfd), |
| 486 #endif | 509 #endif |
| (...skipping 11 matching lines...) Expand all Loading... |
| 498 | 521 |
| 499 void ChildProcessLauncher::Notify(ZygoteHandle zygote, | 522 void ChildProcessLauncher::Notify(ZygoteHandle zygote, |
| 500 #if defined(OS_ANDROID) | 523 #if defined(OS_ANDROID) |
| 501 base::ScopedFD ipcfd, | 524 base::ScopedFD ipcfd, |
| 502 #endif | 525 #endif |
| 503 base::Process process) { | 526 base::Process process) { |
| 504 DCHECK(CalledOnValidThread()); | 527 DCHECK(CalledOnValidThread()); |
| 505 starting_ = false; | 528 starting_ = false; |
| 506 process_ = std::move(process); | 529 process_ = std::move(process); |
| 507 | 530 |
| 531 if (process_.IsValid()) { |
| 532 // Set up Mojo IPC to the new process. |
| 533 mojo::edk::ChildProcessLaunched(process_.Handle(), |
| 534 mojo_platform_channel_.PassServerHandle()); |
| 535 } |
| 536 |
| 508 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID) | 537 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID) |
| 509 zygote_ = zygote; | 538 zygote_ = zygote; |
| 510 #endif | 539 #endif |
| 511 if (process_.IsValid()) { | 540 if (process_.IsValid()) { |
| 512 client_->OnProcessLaunched(); | 541 client_->OnProcessLaunched(); |
| 513 } else { | 542 } else { |
| 514 termination_status_ = base::TERMINATION_STATUS_LAUNCH_FAILED; | 543 termination_status_ = base::TERMINATION_STATUS_LAUNCH_FAILED; |
| 515 client_->OnProcessLaunchFailed(); | 544 client_->OnProcessLaunchFailed(); |
| 516 } | 545 } |
| 517 } | 546 } |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 556 } | 585 } |
| 557 | 586 |
| 558 ChildProcessLauncher::Client* ChildProcessLauncher::ReplaceClientForTest( | 587 ChildProcessLauncher::Client* ChildProcessLauncher::ReplaceClientForTest( |
| 559 Client* client) { | 588 Client* client) { |
| 560 Client* ret = client_; | 589 Client* ret = client_; |
| 561 client_ = client; | 590 client_ = client; |
| 562 return ret; | 591 return ret; |
| 563 } | 592 } |
| 564 | 593 |
| 565 } // namespace content | 594 } // namespace content |
| OLD | NEW |