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