Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 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> // For std::pair. | 7 #include <utility> // For std::pair. |
| 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 14 matching lines...) Expand all Loading... | |
| 25 #include "base/files/file_path.h" | 25 #include "base/files/file_path.h" |
| 26 #include "content/common/sandbox_win.h" | 26 #include "content/common/sandbox_win.h" |
| 27 #include "content/public/common/sandbox_init.h" | 27 #include "content/public/common/sandbox_init.h" |
| 28 #include "content/public/common/sandboxed_process_launcher_delegate.h" | 28 #include "content/public/common/sandboxed_process_launcher_delegate.h" |
| 29 #elif defined(OS_MACOSX) | 29 #elif defined(OS_MACOSX) |
| 30 #include "content/browser/mach_broker_mac.h" | 30 #include "content/browser/mach_broker_mac.h" |
| 31 #elif defined(OS_ANDROID) | 31 #elif defined(OS_ANDROID) |
| 32 #include "base/android/jni_android.h" | 32 #include "base/android/jni_android.h" |
| 33 #include "content/browser/android/child_process_launcher_android.h" | 33 #include "content/browser/android/child_process_launcher_android.h" |
| 34 #elif defined(OS_POSIX) | 34 #elif defined(OS_POSIX) |
| 35 #include "base/memory/shared_memory.h" | |
| 35 #include "base/memory/singleton.h" | 36 #include "base/memory/singleton.h" |
| 36 #include "content/browser/renderer_host/render_sandbox_host_linux.h" | 37 #include "content/browser/renderer_host/render_sandbox_host_linux.h" |
| 37 #include "content/browser/zygote_host/zygote_host_impl_linux.h" | 38 #include "content/browser/zygote_host/zygote_host_impl_linux.h" |
| 38 #include "content/common/child_process_sandbox_support_impl_linux.h" | 39 #include "content/common/child_process_sandbox_support_impl_linux.h" |
| 39 #endif | 40 #endif |
| 40 | 41 |
| 41 #if defined(OS_POSIX) | 42 #if defined(OS_POSIX) |
| 43 #include "base/metrics/stats_table.h" | |
| 42 #include "base/posix/global_descriptors.h" | 44 #include "base/posix/global_descriptors.h" |
| 43 #endif | 45 #endif |
| 44 | 46 |
| 45 namespace content { | 47 namespace content { |
| 46 | 48 |
| 47 // Having the functionality of ChildProcessLauncher be in an internal | 49 // Having the functionality of ChildProcessLauncher be in an internal |
| 48 // ref counted object allows us to automatically terminate the process when the | 50 // ref counted object allows us to automatically terminate the process when the |
| 49 // parent class destructs, while still holding on to state that we need. | 51 // parent class destructs, while still holding on to state that we need. |
| 50 class ChildProcessLauncher::Context | 52 class ChildProcessLauncher::Context |
| 51 : public base::RefCountedThreadSafe<ChildProcessLauncher::Context> { | 53 : public base::RefCountedThreadSafe<ChildProcessLauncher::Context> { |
| (...skipping 12 matching lines...) Expand all Loading... | |
| 64 terminate_child_on_shutdown_ = !CommandLine::ForCurrentProcess()-> | 66 terminate_child_on_shutdown_ = !CommandLine::ForCurrentProcess()-> |
| 65 HasSwitch(switches::kChildCleanExit); | 67 HasSwitch(switches::kChildCleanExit); |
| 66 #else | 68 #else |
| 67 terminate_child_on_shutdown_ = true; | 69 terminate_child_on_shutdown_ = true; |
| 68 #endif | 70 #endif |
| 69 } | 71 } |
| 70 | 72 |
| 71 void Launch( | 73 void Launch( |
| 72 #if defined(OS_WIN) | 74 #if defined(OS_WIN) |
| 73 SandboxedProcessLauncherDelegate* delegate, | 75 SandboxedProcessLauncherDelegate* delegate, |
| 76 bool launch_elevated, | |
| 74 #elif defined(OS_ANDROID) | 77 #elif defined(OS_ANDROID) |
| 75 int ipcfd, | 78 int ipcfd, |
| 76 #elif defined(OS_POSIX) | 79 #elif defined(OS_POSIX) |
| 77 bool use_zygote, | 80 bool use_zygote, |
| 78 const base::EnvironmentMap& environ, | 81 const base::EnvironmentMap& environ, |
| 79 int ipcfd, | 82 int ipcfd, |
| 80 #endif | 83 #endif |
| 81 CommandLine* cmd_line, | 84 CommandLine* cmd_line, |
| 82 int child_process_id, | 85 int child_process_id, |
| 83 Client* client) { | 86 Client* client) { |
| 84 client_ = client; | 87 client_ = client; |
| 85 | 88 |
| 86 CHECK(BrowserThread::GetCurrentThreadIdentifier(&client_thread_id_)); | 89 CHECK(BrowserThread::GetCurrentThreadIdentifier(&client_thread_id_)); |
| 87 | 90 |
| 88 #if defined(OS_ANDROID) | 91 #if defined(OS_ANDROID) |
| 89 // We need to close the client end of the IPC channel to reliably detect | 92 // We need to close the client end of the IPC channel to reliably detect |
| 90 // child termination. We will close this fd after we create the child | 93 // child termination. We will close this fd after we create the child |
| 91 // process which is asynchronous on Android. | 94 // process which is asynchronous on Android. |
| 92 ipcfd_ = ipcfd; | 95 ipcfd_ = ipcfd; |
| 93 #endif | 96 #endif |
| 94 BrowserThread::PostTask( | 97 BrowserThread::PostTask( |
| 95 BrowserThread::PROCESS_LAUNCHER, FROM_HERE, | 98 BrowserThread::PROCESS_LAUNCHER, FROM_HERE, |
| 96 base::Bind( | 99 base::Bind( |
| 97 &Context::LaunchInternal, | 100 &Context::LaunchInternal, |
| 98 make_scoped_refptr(this), | 101 make_scoped_refptr(this), |
| 99 client_thread_id_, | 102 client_thread_id_, |
| 100 child_process_id, | 103 child_process_id, |
| 101 #if defined(OS_WIN) | 104 #if defined(OS_WIN) |
| 102 delegate, | 105 delegate, |
| 106 launch_elevated, | |
| 103 #elif defined(OS_ANDROID) | 107 #elif defined(OS_ANDROID) |
| 104 ipcfd, | 108 ipcfd, |
| 105 #elif defined(OS_POSIX) | 109 #elif defined(OS_POSIX) |
| 106 use_zygote, | 110 use_zygote, |
| 107 environ, | 111 environ, |
| 108 ipcfd, | 112 ipcfd, |
| 109 #endif | 113 #endif |
| 114 | |
|
jam
2014/01/30 21:41:03
nit: drop the empty line
Drew Haven
2014/01/31 20:00:12
Done.
| |
| 110 cmd_line)); | 115 cmd_line)); |
| 111 } | 116 } |
| 112 | 117 |
| 113 #if defined(OS_ANDROID) | 118 #if defined(OS_ANDROID) |
| 114 static void OnChildProcessStarted( | 119 static void OnChildProcessStarted( |
| 115 // |this_object| is NOT thread safe. Only use it to post a task back. | 120 // |this_object| is NOT thread safe. Only use it to post a task back. |
| 116 scoped_refptr<Context> this_object, | 121 scoped_refptr<Context> this_object, |
| 117 BrowserThread::ID client_thread_id, | 122 BrowserThread::ID client_thread_id, |
| 118 const base::TimeTicks begin_launch_time, | 123 const base::TimeTicks begin_launch_time, |
| 119 base::ProcessHandle handle) { | 124 base::ProcessHandle handle) { |
| (...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 176 } | 181 } |
| 177 } | 182 } |
| 178 | 183 |
| 179 static void LaunchInternal( | 184 static void LaunchInternal( |
| 180 // |this_object| is NOT thread safe. Only use it to post a task back. | 185 // |this_object| is NOT thread safe. Only use it to post a task back. |
| 181 scoped_refptr<Context> this_object, | 186 scoped_refptr<Context> this_object, |
| 182 BrowserThread::ID client_thread_id, | 187 BrowserThread::ID client_thread_id, |
| 183 int child_process_id, | 188 int child_process_id, |
| 184 #if defined(OS_WIN) | 189 #if defined(OS_WIN) |
| 185 SandboxedProcessLauncherDelegate* delegate, | 190 SandboxedProcessLauncherDelegate* delegate, |
| 191 bool launch_elevated, | |
| 186 #elif defined(OS_ANDROID) | 192 #elif defined(OS_ANDROID) |
| 187 int ipcfd, | 193 int ipcfd, |
| 188 #elif defined(OS_POSIX) | 194 #elif defined(OS_POSIX) |
| 189 bool use_zygote, | 195 bool use_zygote, |
| 190 const base::EnvironmentMap& env, | 196 const base::EnvironmentMap& env, |
| 191 int ipcfd, | 197 int ipcfd, |
| 192 #endif | 198 #endif |
| 193 CommandLine* cmd_line) { | 199 CommandLine* cmd_line) { |
| 194 scoped_ptr<CommandLine> cmd_line_deleter(cmd_line); | 200 scoped_ptr<CommandLine> cmd_line_deleter(cmd_line); |
| 195 base::TimeTicks begin_launch_time = base::TimeTicks::Now(); | 201 base::TimeTicks begin_launch_time = base::TimeTicks::Now(); |
| 196 | 202 |
| 197 #if defined(OS_WIN) | 203 #if defined(OS_WIN) |
| 198 scoped_ptr<SandboxedProcessLauncherDelegate> delegate_deleter(delegate); | 204 base::ProcessHandle handle = base::kNullProcessHandle; |
| 199 base::ProcessHandle handle = StartSandboxedProcess(delegate, cmd_line); | 205 if (launch_elevated) { |
| 200 #elif defined(OS_ANDROID) | 206 base::LaunchOptions options; |
| 201 // Android WebView runs in single process, ensure that we never get here | 207 options.start_hidden = true; |
| 202 // when running in single process mode. | 208 base::LaunchElevatedProcess(*cmd_line, options, &handle); |
| 203 CHECK(!cmd_line->HasSwitch(switches::kSingleProcess)); | 209 } else { |
| 204 | 210 scoped_ptr<SandboxedProcessLauncherDelegate> delegate_deleter(delegate); |
| 211 handle = StartSandboxedProcess(delegate, cmd_line); | |
| 212 } | |
| 213 #elif defined(OS_POSIX) | |
| 205 std::string process_type = | 214 std::string process_type = |
| 206 cmd_line->GetSwitchValueASCII(switches::kProcessType); | 215 cmd_line->GetSwitchValueASCII(switches::kProcessType); |
| 207 std::vector<FileDescriptorInfo> files_to_register; | 216 std::vector<FileDescriptorInfo> files_to_register; |
| 208 files_to_register.push_back( | 217 files_to_register.push_back( |
| 209 FileDescriptorInfo(kPrimaryIPCChannel, | 218 FileDescriptorInfo(kPrimaryIPCChannel, |
| 210 base::FileDescriptor(ipcfd, false))); | 219 base::FileDescriptor(ipcfd, false))); |
| 220 base::StatsTable* stats_table = base::StatsTable::current(); | |
|
jam
2014/01/30 21:41:03
this, and some of the other changes in this file a
Drew Haven
2014/01/31 20:00:12
Done.
| |
| 221 if (stats_table && | |
| 222 base::SharedMemory::IsHandleValid( | |
| 223 stats_table->GetSharedMemoryHandle())) { | |
| 224 files_to_register.push_back( | |
| 225 FileDescriptorInfo(kStatsTableSharedMemFd, | |
| 226 stats_table->GetSharedMemoryHandle())); | |
| 227 } | |
| 228 #endif | |
| 229 | |
| 230 #if defined(OS_ANDROID) | |
| 231 // Android WebView runs in single process, ensure that we never get here | |
| 232 // when running in single process mode. | |
| 233 CHECK(!cmd_line->HasSwitch(switches::kSingleProcess)); | |
| 211 | 234 |
| 212 GetContentClient()->browser()-> | 235 GetContentClient()->browser()-> |
| 213 GetAdditionalMappedFilesForChildProcess(*cmd_line, child_process_id, | 236 GetAdditionalMappedFilesForChildProcess(*cmd_line, child_process_id, |
| 214 &files_to_register); | 237 &files_to_register); |
| 215 | 238 |
| 216 StartChildProcess(cmd_line->argv(), files_to_register, | 239 StartChildProcess(cmd_line->argv(), files_to_register, |
| 217 base::Bind(&ChildProcessLauncher::Context::OnChildProcessStarted, | 240 base::Bind(&ChildProcessLauncher::Context::OnChildProcessStarted, |
| 218 this_object, client_thread_id, begin_launch_time)); | 241 this_object, client_thread_id, begin_launch_time)); |
| 219 | 242 |
| 220 #elif defined(OS_POSIX) | 243 #elif defined(OS_POSIX) |
| 221 base::ProcessHandle handle = base::kNullProcessHandle; | 244 base::ProcessHandle handle = base::kNullProcessHandle; |
| 222 // We need to close the client end of the IPC channel to reliably detect | 245 // We need to close the client end of the IPC channel to reliably detect |
| 223 // child termination. | 246 // child termination. |
| 224 file_util::ScopedFD ipcfd_closer(&ipcfd); | 247 file_util::ScopedFD ipcfd_closer(&ipcfd); |
| 225 | 248 |
| 226 std::string process_type = | |
| 227 cmd_line->GetSwitchValueASCII(switches::kProcessType); | |
| 228 std::vector<FileDescriptorInfo> files_to_register; | |
| 229 files_to_register.push_back( | |
| 230 FileDescriptorInfo(kPrimaryIPCChannel, | |
| 231 base::FileDescriptor(ipcfd, false))); | |
| 232 | |
| 233 #if !defined(OS_MACOSX) | 249 #if !defined(OS_MACOSX) |
| 234 GetContentClient()->browser()-> | 250 GetContentClient()->browser()-> |
| 235 GetAdditionalMappedFilesForChildProcess(*cmd_line, child_process_id, | 251 GetAdditionalMappedFilesForChildProcess(*cmd_line, child_process_id, |
| 236 &files_to_register); | 252 &files_to_register); |
| 237 if (use_zygote) { | 253 if (use_zygote) { |
| 238 handle = ZygoteHostImpl::GetInstance()->ForkRequest(cmd_line->argv(), | 254 handle = ZygoteHostImpl::GetInstance()->ForkRequest(cmd_line->argv(), |
| 239 files_to_register, | 255 files_to_register, |
| 240 process_type); | 256 process_type); |
| 241 } else | 257 } else |
| 242 // Fall through to the normal posix case below when we're not zygoting. | 258 // Fall through to the normal posix case below when we're not zygoting. |
| (...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 323 #endif | 339 #endif |
| 324 starting_ = false; | 340 starting_ = false; |
| 325 process_.set_handle(handle); | 341 process_.set_handle(handle); |
| 326 if (!handle) | 342 if (!handle) |
| 327 LOG(ERROR) << "Failed to launch child process"; | 343 LOG(ERROR) << "Failed to launch child process"; |
| 328 | 344 |
| 329 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID) | 345 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID) |
| 330 zygote_ = zygote; | 346 zygote_ = zygote; |
| 331 #endif | 347 #endif |
| 332 if (client_) { | 348 if (client_) { |
| 333 client_->OnProcessLaunched(); | 349 if (handle) { |
| 350 client_->OnProcessLaunched(); | |
| 351 } else { | |
| 352 client_->OnProcessLaunchFailed(); | |
| 353 } | |
| 334 } else { | 354 } else { |
| 335 Terminate(); | 355 Terminate(); |
| 336 } | 356 } |
| 337 } | 357 } |
| 338 | 358 |
| 339 void Terminate() { | 359 void Terminate() { |
| 340 if (!process_.handle()) | 360 if (!process_.handle()) |
| 341 return; | 361 return; |
| 342 | 362 |
| 343 if (!terminate_child_on_shutdown_) | 363 if (!terminate_child_on_shutdown_) |
| (...skipping 17 matching lines...) Expand all Loading... | |
| 361 base::Process process(handle); | 381 base::Process process(handle); |
| 362 process.SetProcessBackgrounded(background); | 382 process.SetProcessBackgrounded(background); |
| 363 } | 383 } |
| 364 | 384 |
| 365 static void TerminateInternal( | 385 static void TerminateInternal( |
| 366 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID) | 386 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID) |
| 367 bool zygote, | 387 bool zygote, |
| 368 #endif | 388 #endif |
| 369 base::ProcessHandle handle) { | 389 base::ProcessHandle handle) { |
| 370 #if defined(OS_ANDROID) | 390 #if defined(OS_ANDROID) |
| 371 LOG(INFO) << "ChromeProcess: Stopping process with handle " << handle; | 391 VLOG(0) << "ChromeProcess: Stopping process with handle " << handle; |
| 372 StopChildProcess(handle); | 392 StopChildProcess(handle); |
| 373 #else | 393 #else |
| 374 base::Process process(handle); | 394 base::Process process(handle); |
| 375 // Client has gone away, so just kill the process. Using exit code 0 | 395 // Client has gone away, so just kill the process. Using exit code 0 |
| 376 // means that UMA won't treat this as a crash. | 396 // means that UMA won't treat this as a crash. |
| 377 process.Terminate(RESULT_CODE_NORMAL_EXIT); | 397 process.Terminate(RESULT_CODE_NORMAL_EXIT); |
| 378 // On POSIX, we must additionally reap the child. | 398 // On POSIX, we must additionally reap the child. |
| 379 #if defined(OS_POSIX) | 399 #if defined(OS_POSIX) |
| 380 #if !defined(OS_MACOSX) | 400 #if !defined(OS_MACOSX) |
| 381 if (zygote) { | 401 if (zygote) { |
| (...skipping 24 matching lines...) Expand all Loading... | |
| 406 int ipcfd_; | 426 int ipcfd_; |
| 407 #elif defined(OS_POSIX) && !defined(OS_MACOSX) | 427 #elif defined(OS_POSIX) && !defined(OS_MACOSX) |
| 408 bool zygote_; | 428 bool zygote_; |
| 409 #endif | 429 #endif |
| 410 }; | 430 }; |
| 411 | 431 |
| 412 | 432 |
| 413 ChildProcessLauncher::ChildProcessLauncher( | 433 ChildProcessLauncher::ChildProcessLauncher( |
| 414 #if defined(OS_WIN) | 434 #if defined(OS_WIN) |
| 415 SandboxedProcessLauncherDelegate* delegate, | 435 SandboxedProcessLauncherDelegate* delegate, |
| 436 bool launch_elevated, | |
| 416 #elif defined(OS_POSIX) | 437 #elif defined(OS_POSIX) |
| 417 bool use_zygote, | 438 bool use_zygote, |
| 418 const base::EnvironmentMap& environ, | 439 const base::EnvironmentMap& environ, |
| 419 int ipcfd, | 440 int ipcfd, |
| 420 #endif | 441 #endif |
| 421 CommandLine* cmd_line, | 442 CommandLine* cmd_line, |
| 422 int child_process_id, | 443 int child_process_id, |
| 423 Client* client) { | 444 Client* client) { |
| 424 context_ = new Context(); | 445 context_ = new Context(); |
| 425 context_->Launch( | 446 context_->Launch( |
| 426 #if defined(OS_WIN) | 447 #if defined(OS_WIN) |
| 427 delegate, | 448 delegate, |
| 449 launch_elevated, | |
| 428 #elif defined(OS_ANDROID) | 450 #elif defined(OS_ANDROID) |
| 429 ipcfd, | 451 ipcfd, |
| 430 #elif defined(OS_POSIX) | 452 #elif defined(OS_POSIX) |
| 431 use_zygote, | 453 use_zygote, |
| 432 environ, | 454 environ, |
| 433 ipcfd, | 455 ipcfd, |
| 434 #endif | 456 #endif |
| 435 cmd_line, | 457 cmd_line, |
| 436 child_process_id, | 458 child_process_id, |
| 437 client); | 459 client); |
| (...skipping 19 matching lines...) Expand all Loading... | |
| 457 if (handle == base::kNullProcessHandle) { | 479 if (handle == base::kNullProcessHandle) { |
| 458 // Process is already gone, so return the cached termination status. | 480 // Process is already gone, so return the cached termination status. |
| 459 if (exit_code) | 481 if (exit_code) |
| 460 *exit_code = context_->exit_code_; | 482 *exit_code = context_->exit_code_; |
| 461 return context_->termination_status_; | 483 return context_->termination_status_; |
| 462 } | 484 } |
| 463 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID) | 485 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID) |
| 464 if (context_->zygote_) { | 486 if (context_->zygote_) { |
| 465 context_->termination_status_ = ZygoteHostImpl::GetInstance()-> | 487 context_->termination_status_ = ZygoteHostImpl::GetInstance()-> |
| 466 GetTerminationStatus(handle, known_dead, &context_->exit_code_); | 488 GetTerminationStatus(handle, known_dead, &context_->exit_code_); |
| 467 } else | 489 } else if (known_dead) { |
| 490 context_->termination_status_ = | |
| 491 base::GetKnownDeadTerminationStatus(handle, &context_->exit_code_); | |
| 492 } else { | |
| 468 #elif defined(OS_MACOSX) | 493 #elif defined(OS_MACOSX) |
| 469 if (known_dead) { | 494 if (known_dead) { |
| 470 context_->termination_status_ = | 495 context_->termination_status_ = |
| 471 base::GetKnownDeadTerminationStatus(handle, &context_->exit_code_); | 496 base::GetKnownDeadTerminationStatus(handle, &context_->exit_code_); |
| 472 } else | 497 } else { |
| 498 #elif defined(OS_ANDROID) | |
| 499 if (IsChildProcessOomProtected(handle)) { | |
| 500 context_->termination_status_ = base::TERMINATION_STATUS_OOM_PROTECTED; | |
| 501 } else { | |
| 502 #else | |
| 503 { | |
| 473 #endif | 504 #endif |
| 474 { | |
| 475 context_->termination_status_ = | 505 context_->termination_status_ = |
| 476 base::GetTerminationStatus(handle, &context_->exit_code_); | 506 base::GetTerminationStatus(handle, &context_->exit_code_); |
| 477 } | 507 } |
| 478 | 508 |
| 479 if (exit_code) | 509 if (exit_code) |
| 480 *exit_code = context_->exit_code_; | 510 *exit_code = context_->exit_code_; |
| 481 | 511 |
| 482 // POSIX: If the process crashed, then the kernel closed the socket | 512 // POSIX: If the process crashed, then the kernel closed the socket |
| 483 // for it and so the child has already died by the time we get | 513 // for it and so the child has already died by the time we get |
| 484 // here. Since GetTerminationStatus called waitpid with WNOHANG, | 514 // here. Since GetTerminationStatus called waitpid with WNOHANG, |
| (...skipping 14 matching lines...) Expand all Loading... | |
| 499 GetHandle(), background)); | 529 GetHandle(), background)); |
| 500 } | 530 } |
| 501 | 531 |
| 502 void ChildProcessLauncher::SetTerminateChildOnShutdown( | 532 void ChildProcessLauncher::SetTerminateChildOnShutdown( |
| 503 bool terminate_on_shutdown) { | 533 bool terminate_on_shutdown) { |
| 504 if (context_.get()) | 534 if (context_.get()) |
| 505 context_->set_terminate_child_on_shutdown(terminate_on_shutdown); | 535 context_->set_terminate_child_on_shutdown(terminate_on_shutdown); |
| 506 } | 536 } |
| 507 | 537 |
| 508 } // namespace content | 538 } // namespace content |
| OLD | NEW |