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" |
(...skipping 21 matching lines...) Expand all Loading... | |
32 #include "content/browser/bootstrap_sandbox_manager_mac.h" | 32 #include "content/browser/bootstrap_sandbox_manager_mac.h" |
33 #include "content/browser/mach_broker_mac.h" | 33 #include "content/browser/mach_broker_mac.h" |
34 #include "sandbox/mac/bootstrap_sandbox.h" | 34 #include "sandbox/mac/bootstrap_sandbox.h" |
35 #include "sandbox/mac/pre_exec_delegate.h" | 35 #include "sandbox/mac/pre_exec_delegate.h" |
36 #elif defined(OS_ANDROID) | 36 #elif defined(OS_ANDROID) |
37 #include "base/android/jni_android.h" | 37 #include "base/android/jni_android.h" |
38 #include "content/browser/android/child_process_launcher_android.h" | 38 #include "content/browser/android/child_process_launcher_android.h" |
39 #elif defined(OS_POSIX) | 39 #elif defined(OS_POSIX) |
40 #include "base/memory/singleton.h" | 40 #include "base/memory/singleton.h" |
41 #include "content/browser/renderer_host/render_sandbox_host_linux.h" | 41 #include "content/browser/renderer_host/render_sandbox_host_linux.h" |
42 #include "content/browser/zygote_host/zygote_communication_linux.h" | |
42 #include "content/browser/zygote_host/zygote_host_impl_linux.h" | 43 #include "content/browser/zygote_host/zygote_host_impl_linux.h" |
43 #include "content/common/child_process_sandbox_support_impl_linux.h" | 44 #include "content/common/child_process_sandbox_support_impl_linux.h" |
44 #endif | 45 #endif |
45 | 46 |
46 #if defined(OS_POSIX) | 47 #if defined(OS_POSIX) |
47 #include "base/posix/global_descriptors.h" | 48 #include "base/posix/global_descriptors.h" |
48 #include "content/browser/file_descriptor_info_impl.h" | 49 #include "content/browser/file_descriptor_info_impl.h" |
49 #include "gin/v8_initializer.h" | 50 #include "gin/v8_initializer.h" |
50 #endif | 51 #endif |
51 | 52 |
52 namespace content { | 53 namespace content { |
53 | 54 |
54 namespace { | 55 namespace { |
55 | 56 |
56 typedef base::Callback<void(bool, | 57 typedef base::Callback<void(ZygoteHandle, |
57 #if defined(OS_ANDROID) | 58 #if defined(OS_ANDROID) |
58 base::ScopedFD, | 59 base::ScopedFD, |
59 #endif | 60 #endif |
60 base::Process)> NotifyCallback; | 61 base::Process)> NotifyCallback; |
61 | 62 |
62 void RecordHistogramsOnLauncherThread(base::TimeDelta launch_time) { | 63 void RecordHistogramsOnLauncherThread(base::TimeDelta launch_time) { |
63 DCHECK_CURRENTLY_ON(BrowserThread::PROCESS_LAUNCHER); | 64 DCHECK_CURRENTLY_ON(BrowserThread::PROCESS_LAUNCHER); |
64 // Log the launch time, separating out the first one (which will likely be | 65 // Log the launch time, separating out the first one (which will likely be |
65 // slower due to the rest of the browser initializing at the same time). | 66 // slower due to the rest of the browser initializing at the same time). |
66 static bool done_first_launch = false; | 67 static bool done_first_launch = false; |
(...skipping 13 matching lines...) Expand all Loading... | |
80 const base::TimeTicks begin_launch_time, | 81 const base::TimeTicks begin_launch_time, |
81 base::ScopedFD ipcfd, | 82 base::ScopedFD ipcfd, |
82 base::ProcessHandle handle) { | 83 base::ProcessHandle handle) { |
83 // This can be called on the launcher thread or UI thread. | 84 // This can be called on the launcher thread or UI thread. |
84 base::TimeDelta launch_time = base::TimeTicks::Now() - begin_launch_time; | 85 base::TimeDelta launch_time = base::TimeTicks::Now() - begin_launch_time; |
85 BrowserThread::PostTask( | 86 BrowserThread::PostTask( |
86 BrowserThread::PROCESS_LAUNCHER, FROM_HERE, | 87 BrowserThread::PROCESS_LAUNCHER, FROM_HERE, |
87 base::Bind(&RecordHistogramsOnLauncherThread, launch_time)); | 88 base::Bind(&RecordHistogramsOnLauncherThread, launch_time)); |
88 | 89 |
89 base::Closure callback_on_client_thread( | 90 base::Closure callback_on_client_thread( |
90 base::Bind(callback, false, base::Passed(&ipcfd), | 91 base::Bind(callback, nullptr, base::Passed(&ipcfd), |
91 base::Passed(base::Process(handle)))); | 92 base::Passed(base::Process(handle)))); |
92 if (BrowserThread::CurrentlyOn(client_thread_id)) { | 93 if (BrowserThread::CurrentlyOn(client_thread_id)) { |
93 callback_on_client_thread.Run(); | 94 callback_on_client_thread.Run(); |
94 } else { | 95 } else { |
95 BrowserThread::PostTask( | 96 BrowserThread::PostTask( |
96 client_thread_id, FROM_HERE, callback_on_client_thread); | 97 client_thread_id, FROM_HERE, callback_on_client_thread); |
97 } | 98 } |
98 } | 99 } |
99 #endif | 100 #endif |
100 | 101 |
101 void LaunchOnLauncherThread(const NotifyCallback& callback, | 102 void LaunchOnLauncherThread(const NotifyCallback& callback, |
102 BrowserThread::ID client_thread_id, | 103 BrowserThread::ID client_thread_id, |
103 int child_process_id, | 104 int child_process_id, |
104 SandboxedProcessLauncherDelegate* delegate, | 105 SandboxedProcessLauncherDelegate* delegate, |
105 #if defined(OS_ANDROID) | 106 #if defined(OS_ANDROID) |
106 base::ScopedFD ipcfd, | 107 base::ScopedFD ipcfd, |
107 #endif | 108 #endif |
108 base::CommandLine* cmd_line) { | 109 base::CommandLine* cmd_line) { |
109 DCHECK_CURRENTLY_ON(BrowserThread::PROCESS_LAUNCHER); | 110 DCHECK_CURRENTLY_ON(BrowserThread::PROCESS_LAUNCHER); |
110 scoped_ptr<SandboxedProcessLauncherDelegate> delegate_deleter(delegate); | 111 scoped_ptr<SandboxedProcessLauncherDelegate> delegate_deleter(delegate); |
112 #if !defined(OS_ANDROID) | |
113 ZygoteHandle zygote = nullptr; | |
114 #endif | |
111 #if defined(OS_WIN) | 115 #if defined(OS_WIN) |
112 bool use_zygote = false; | |
113 bool launch_elevated = delegate->ShouldLaunchElevated(); | 116 bool launch_elevated = delegate->ShouldLaunchElevated(); |
114 #elif defined(OS_MACOSX) | 117 #elif defined(OS_MACOSX) |
115 bool use_zygote = false; | |
116 base::EnvironmentMap env = delegate->GetEnvironment(); | 118 base::EnvironmentMap env = delegate->GetEnvironment(); |
117 base::ScopedFD ipcfd = delegate->TakeIpcFd(); | 119 base::ScopedFD ipcfd = delegate->TakeIpcFd(); |
118 #elif defined(OS_POSIX) && !defined(OS_ANDROID) | 120 #elif defined(OS_POSIX) && !defined(OS_ANDROID) |
119 bool use_zygote = delegate->ShouldUseZygote(); | |
120 base::EnvironmentMap env = delegate->GetEnvironment(); | 121 base::EnvironmentMap env = delegate->GetEnvironment(); |
121 base::ScopedFD ipcfd = delegate->TakeIpcFd(); | 122 base::ScopedFD ipcfd = delegate->TakeIpcFd(); |
122 #endif | 123 #endif |
123 scoped_ptr<base::CommandLine> cmd_line_deleter(cmd_line); | 124 scoped_ptr<base::CommandLine> cmd_line_deleter(cmd_line); |
124 base::TimeTicks begin_launch_time = base::TimeTicks::Now(); | 125 base::TimeTicks begin_launch_time = base::TimeTicks::Now(); |
125 | 126 |
126 base::Process process; | 127 base::Process process; |
127 #if defined(OS_WIN) | 128 #if defined(OS_WIN) |
128 if (launch_elevated) { | 129 if (launch_elevated) { |
129 base::LaunchOptions options; | 130 base::LaunchOptions options; |
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
192 StartChildProcess( | 193 StartChildProcess( |
193 cmd_line->argv(), child_process_id, std::move(files_to_register), regions, | 194 cmd_line->argv(), child_process_id, std::move(files_to_register), regions, |
194 base::Bind(&OnChildProcessStartedAndroid, callback, client_thread_id, | 195 base::Bind(&OnChildProcessStartedAndroid, callback, client_thread_id, |
195 begin_launch_time, base::Passed(&ipcfd))); | 196 begin_launch_time, base::Passed(&ipcfd))); |
196 | 197 |
197 #elif defined(OS_POSIX) | 198 #elif defined(OS_POSIX) |
198 // We need to close the client end of the IPC channel to reliably detect | 199 // We need to close the client end of the IPC channel to reliably detect |
199 // child termination. | 200 // child termination. |
200 | 201 |
201 #if !defined(OS_MACOSX) | 202 #if !defined(OS_MACOSX) |
202 if (use_zygote) { | 203 ZygoteHandle* zygote_handle = delegate->GetZygote(); |
203 base::ProcessHandle handle = ZygoteHostImpl::GetInstance()->ForkRequest( | 204 // If |zygote_handle| is null, a zygote should not be used. |
205 if (zygote_handle) { | |
206 // This code runs on the PROCESS_LAUNCHER thread so race conditions are not | |
207 // an issue with the lazy initialization. | |
208 if (*zygote_handle == nullptr) { | |
Mark Seaborn
2016/01/19 21:06:43
So there is still this code for launching the zygo
mdempsky
2016/01/19 21:26:43
It's not currently used, but we'll start using it
Greg K
2016/01/19 22:20:16
I talked to Matt, except for NaCl, the zygotes are
| |
209 *zygote_handle = new ZygoteCommunication(); | |
210 (*zygote_handle)->Init(); | |
211 } | |
212 zygote = *zygote_handle; | |
213 base::ProcessHandle handle = zygote->ForkRequest( | |
204 cmd_line->argv(), std::move(files_to_register), process_type); | 214 cmd_line->argv(), std::move(files_to_register), process_type); |
205 process = base::Process(handle); | 215 process = base::Process(handle); |
206 } else | 216 } else |
207 // Fall through to the normal posix case below when we're not zygoting. | 217 // Fall through to the normal posix case below when we're not zygoting. |
208 #endif // !defined(OS_MACOSX) | 218 #endif // !defined(OS_MACOSX) |
209 { | 219 { |
210 // Convert FD mapping to FileHandleMappingVector | 220 // Convert FD mapping to FileHandleMappingVector |
211 base::FileHandleMappingVector fds_to_map = | 221 base::FileHandleMappingVector fds_to_map = |
212 files_to_register->GetMappingWithIDAdjustment( | 222 files_to_register->GetMappingWithIDAdjustment( |
213 base::GlobalDescriptors::kBaseDescriptor); | 223 base::GlobalDescriptors::kBaseDescriptor); |
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
272 broker->GetLock().Release(); | 282 broker->GetLock().Release(); |
273 #endif // defined(OS_MACOSX) | 283 #endif // defined(OS_MACOSX) |
274 } | 284 } |
275 #endif // else defined(OS_POSIX) | 285 #endif // else defined(OS_POSIX) |
276 #if !defined(OS_ANDROID) | 286 #if !defined(OS_ANDROID) |
277 if (process.IsValid()) { | 287 if (process.IsValid()) { |
278 RecordHistogramsOnLauncherThread(base::TimeTicks::Now() - | 288 RecordHistogramsOnLauncherThread(base::TimeTicks::Now() - |
279 begin_launch_time); | 289 begin_launch_time); |
280 } | 290 } |
281 BrowserThread::PostTask(client_thread_id, FROM_HERE, | 291 BrowserThread::PostTask(client_thread_id, FROM_HERE, |
282 base::Bind(callback, | 292 base::Bind(callback, zygote, base::Passed(&process))); |
283 use_zygote, | |
284 base::Passed(&process))); | |
285 #endif // !defined(OS_ANDROID) | 293 #endif // !defined(OS_ANDROID) |
286 } | 294 } |
287 | 295 |
288 void TerminateOnLauncherThread(bool zygote, base::Process process) { | 296 void TerminateOnLauncherThread(ZygoteHandle zygote, base::Process process) { |
289 DCHECK_CURRENTLY_ON(BrowserThread::PROCESS_LAUNCHER); | 297 DCHECK_CURRENTLY_ON(BrowserThread::PROCESS_LAUNCHER); |
290 #if defined(OS_ANDROID) | 298 #if defined(OS_ANDROID) |
291 VLOG(1) << "ChromeProcess: Stopping process with handle " | 299 VLOG(1) << "ChromeProcess: Stopping process with handle " |
292 << process.Handle(); | 300 << process.Handle(); |
293 StopChildProcess(process.Handle()); | 301 StopChildProcess(process.Handle()); |
294 #else | 302 #else |
295 // Client has gone away, so just kill the process. Using exit code 0 | 303 // Client has gone away, so just kill the process. Using exit code 0 |
296 // means that UMA won't treat this as a crash. | 304 // means that UMA won't treat this as a crash. |
297 process.Terminate(RESULT_CODE_NORMAL_EXIT, false); | 305 process.Terminate(RESULT_CODE_NORMAL_EXIT, false); |
298 // On POSIX, we must additionally reap the child. | 306 // On POSIX, we must additionally reap the child. |
299 #if defined(OS_POSIX) | 307 #if defined(OS_POSIX) |
300 #if !defined(OS_MACOSX) | 308 #if !defined(OS_MACOSX) |
301 if (zygote) { | 309 if (zygote) { |
302 // If the renderer was created via a zygote, we have to proxy the reaping | 310 // If the renderer was created via a zygote, we have to proxy the reaping |
303 // through the zygote process. | 311 // through the zygote process. |
304 ZygoteHostImpl::GetInstance()->EnsureProcessTerminated(process.Handle()); | 312 zygote->EnsureProcessTerminated(process.Handle()); |
305 } else | 313 } else |
306 #endif // !OS_MACOSX | 314 #endif // !OS_MACOSX |
307 base::EnsureProcessTerminated(std::move(process)); | 315 base::EnsureProcessTerminated(std::move(process)); |
308 #endif // OS_POSIX | 316 #endif // OS_POSIX |
309 #endif // defined(OS_ANDROID) | 317 #endif // defined(OS_ANDROID) |
310 } | 318 } |
311 | 319 |
312 void SetProcessBackgroundedOnLauncherThread(base::Process process, | 320 void SetProcessBackgroundedOnLauncherThread(base::Process process, |
313 bool background) { | 321 bool background) { |
314 DCHECK_CURRENTLY_ON(BrowserThread::PROCESS_LAUNCHER); | 322 DCHECK_CURRENTLY_ON(BrowserThread::PROCESS_LAUNCHER); |
315 if (process.CanBackgroundProcesses()) { | 323 if (process.CanBackgroundProcesses()) { |
316 process.SetProcessBackgrounded(background); | 324 process.SetProcessBackgrounded(background); |
317 } | 325 } |
318 #if defined(OS_ANDROID) | 326 #if defined(OS_ANDROID) |
319 SetChildProcessInForeground(process.Handle(), !background); | 327 SetChildProcessInForeground(process.Handle(), !background); |
320 #endif | 328 #endif |
321 } | 329 } |
322 | 330 |
323 } // namespace | 331 } // namespace |
324 | 332 |
325 ChildProcessLauncher::ChildProcessLauncher( | 333 ChildProcessLauncher::ChildProcessLauncher( |
326 SandboxedProcessLauncherDelegate* delegate, | 334 SandboxedProcessLauncherDelegate* delegate, |
327 base::CommandLine* cmd_line, | 335 base::CommandLine* cmd_line, |
328 int child_process_id, | 336 int child_process_id, |
329 Client* client, | 337 Client* client, |
330 bool terminate_on_shutdown) | 338 bool terminate_on_shutdown) |
331 : client_(client), | 339 : client_(client), |
332 termination_status_(base::TERMINATION_STATUS_NORMAL_TERMINATION), | 340 termination_status_(base::TERMINATION_STATUS_NORMAL_TERMINATION), |
333 exit_code_(RESULT_CODE_NORMAL_EXIT), | 341 exit_code_(RESULT_CODE_NORMAL_EXIT), |
334 zygote_(false), | 342 zygote_(nullptr), |
335 starting_(true), | 343 starting_(true), |
336 #if defined(ADDRESS_SANITIZER) || defined(LEAK_SANITIZER) || \ | 344 #if defined(ADDRESS_SANITIZER) || defined(LEAK_SANITIZER) || \ |
337 defined(MEMORY_SANITIZER) || defined(THREAD_SANITIZER) || \ | 345 defined(MEMORY_SANITIZER) || defined(THREAD_SANITIZER) || \ |
338 defined(UNDEFINED_SANITIZER) | 346 defined(UNDEFINED_SANITIZER) |
339 terminate_child_on_shutdown_(false), | 347 terminate_child_on_shutdown_(false), |
340 #else | 348 #else |
341 terminate_child_on_shutdown_(terminate_on_shutdown), | 349 terminate_child_on_shutdown_(terminate_on_shutdown), |
342 #endif | 350 #endif |
343 weak_factory_(this) { | 351 weak_factory_(this) { |
344 DCHECK(CalledOnValidThread()); | 352 DCHECK(CalledOnValidThread()); |
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
394 #if defined(OS_ANDROID) | 402 #if defined(OS_ANDROID) |
395 base::Passed(&ipcfd), | 403 base::Passed(&ipcfd), |
396 #endif | 404 #endif |
397 cmd_line)); | 405 cmd_line)); |
398 } | 406 } |
399 | 407 |
400 void ChildProcessLauncher::UpdateTerminationStatus(bool known_dead) { | 408 void ChildProcessLauncher::UpdateTerminationStatus(bool known_dead) { |
401 DCHECK(CalledOnValidThread()); | 409 DCHECK(CalledOnValidThread()); |
402 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID) | 410 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID) |
403 if (zygote_) { | 411 if (zygote_) { |
404 termination_status_ = ZygoteHostImpl::GetInstance()-> | 412 termination_status_ = zygote_->GetTerminationStatus( |
405 GetTerminationStatus(process_.Handle(), known_dead, &exit_code_); | 413 process_.Handle(), known_dead, &exit_code_); |
406 } else if (known_dead) { | 414 } else if (known_dead) { |
407 termination_status_ = | 415 termination_status_ = |
408 base::GetKnownDeadTerminationStatus(process_.Handle(), &exit_code_); | 416 base::GetKnownDeadTerminationStatus(process_.Handle(), &exit_code_); |
409 } else { | 417 } else { |
410 #elif defined(OS_MACOSX) | 418 #elif defined(OS_MACOSX) |
411 if (known_dead) { | 419 if (known_dead) { |
412 termination_status_ = | 420 termination_status_ = |
413 base::GetKnownDeadTerminationStatus(process_.Handle(), &exit_code_); | 421 base::GetKnownDeadTerminationStatus(process_.Handle(), &exit_code_); |
414 } else { | 422 } else { |
415 #elif defined(OS_ANDROID) | 423 #elif defined(OS_ANDROID) |
(...skipping 12 matching lines...) Expand all Loading... | |
428 DCHECK(CalledOnValidThread()); | 436 DCHECK(CalledOnValidThread()); |
429 base::Process to_pass = process_.Duplicate(); | 437 base::Process to_pass = process_.Duplicate(); |
430 BrowserThread::PostTask(BrowserThread::PROCESS_LAUNCHER, FROM_HERE, | 438 BrowserThread::PostTask(BrowserThread::PROCESS_LAUNCHER, FROM_HERE, |
431 base::Bind(&SetProcessBackgroundedOnLauncherThread, | 439 base::Bind(&SetProcessBackgroundedOnLauncherThread, |
432 base::Passed(&to_pass), background)); | 440 base::Passed(&to_pass), background)); |
433 } | 441 } |
434 | 442 |
435 void ChildProcessLauncher::DidLaunch( | 443 void ChildProcessLauncher::DidLaunch( |
436 base::WeakPtr<ChildProcessLauncher> instance, | 444 base::WeakPtr<ChildProcessLauncher> instance, |
437 bool terminate_on_shutdown, | 445 bool terminate_on_shutdown, |
438 bool zygote, | 446 ZygoteHandle zygote, |
439 #if defined(OS_ANDROID) | 447 #if defined(OS_ANDROID) |
440 base::ScopedFD ipcfd, | 448 base::ScopedFD ipcfd, |
441 #endif | 449 #endif |
442 base::Process process) { | 450 base::Process process) { |
443 if (!process.IsValid()) | 451 if (!process.IsValid()) |
444 LOG(ERROR) << "Failed to launch child process"; | 452 LOG(ERROR) << "Failed to launch child process"; |
445 | 453 |
446 if (instance.get()) { | 454 if (instance.get()) { |
447 instance->Notify(zygote, | 455 instance->Notify(zygote, |
448 #if defined(OS_ANDROID) | 456 #if defined(OS_ANDROID) |
449 std::move(ipcfd), | 457 std::move(ipcfd), |
450 #endif | 458 #endif |
451 std::move(process)); | 459 std::move(process)); |
452 } else { | 460 } else { |
453 if (process.IsValid() && terminate_on_shutdown) { | 461 if (process.IsValid() && terminate_on_shutdown) { |
454 // On Posix, EnsureProcessTerminated can lead to 2 seconds of sleep! So | 462 // On Posix, EnsureProcessTerminated can lead to 2 seconds of sleep! So |
455 // don't this on the UI/IO threads. | 463 // don't this on the UI/IO threads. |
456 BrowserThread::PostTask(BrowserThread::PROCESS_LAUNCHER, FROM_HERE, | 464 BrowserThread::PostTask(BrowserThread::PROCESS_LAUNCHER, FROM_HERE, |
457 base::Bind(&TerminateOnLauncherThread, zygote, | 465 base::Bind(&TerminateOnLauncherThread, zygote, |
458 base::Passed(&process))); | 466 base::Passed(&process))); |
459 } | 467 } |
460 } | 468 } |
461 } | 469 } |
462 | 470 |
463 void ChildProcessLauncher::Notify( | 471 void ChildProcessLauncher::Notify(ZygoteHandle zygote, |
464 bool zygote, | |
465 #if defined(OS_ANDROID) | 472 #if defined(OS_ANDROID) |
466 base::ScopedFD ipcfd, | 473 base::ScopedFD ipcfd, |
467 #endif | 474 #endif |
468 base::Process process) { | 475 base::Process process) { |
469 DCHECK(CalledOnValidThread()); | 476 DCHECK(CalledOnValidThread()); |
470 starting_ = false; | 477 starting_ = false; |
471 process_ = std::move(process); | 478 process_ = std::move(process); |
472 | 479 |
473 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID) | 480 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID) |
474 zygote_ = zygote; | 481 zygote_ = zygote; |
475 #endif | 482 #endif |
476 if (process_.IsValid()) { | 483 if (process_.IsValid()) { |
477 client_->OnProcessLaunched(); | 484 client_->OnProcessLaunched(); |
478 } else { | 485 } else { |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
521 } | 528 } |
522 | 529 |
523 ChildProcessLauncher::Client* ChildProcessLauncher::ReplaceClientForTest( | 530 ChildProcessLauncher::Client* ChildProcessLauncher::ReplaceClientForTest( |
524 Client* client) { | 531 Client* client) { |
525 Client* ret = client_; | 532 Client* ret = client_; |
526 client_ = client; | 533 client_ = client; |
527 return ret; | 534 return ret; |
528 } | 535 } |
529 | 536 |
530 } // namespace content | 537 } // namespace content |
OLD | NEW |