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