Unifying ChildProcessLauncher across platforms.
Refactored ChildProcessLauncher in an attempt to unify the API across
platforms and split the implementation in platform specific files,
replacing the existing ifdef's spaghetti code.
A new ref counted Helper class is added to ChildProcessLauncher that is
used with the various posted tasks (ChildProcessLauncher is owned by the
client code and can be destroyed at any point) and contains most of the
platform specific code.
In some cases, some minimal amount of code is duplicated in the platform
specific files, but it keeps the code clearer and easier to follow. Non
trivial shared code between Posix implementations is in
child_process_launcher_posix.cc.
BUG=675294
Review-Url: https://codereview.chromium.org/2594203004
Cr-Commit-Position: refs/heads/master@{#444442}
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index fcb2e27c..826786ab 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -412,6 +412,14 @@
"cache_storage/cache_storage_scheduler_client.h",
"child_process_launcher.cc",
"child_process_launcher.h",
+ "child_process_launcher_helper.cc",
+ "child_process_launcher_helper.h",
+ "child_process_launcher_helper_android.cc",
+ "child_process_launcher_helper_linux.cc",
+ "child_process_launcher_helper_mac.cc",
+ "child_process_launcher_helper_posix.cc",
+ "child_process_launcher_helper_posix.h",
+ "child_process_launcher_helper_win.cc",
"child_process_security_policy_impl.cc",
"child_process_security_policy_impl.h",
"cocoa/system_hotkey_helper_mac.h",
diff --git a/content/browser/android/child_process_launcher_android.cc b/content/browser/android/child_process_launcher_android.cc
index a196b1a..ceb9a28b 100644
--- a/content/browser/android/child_process_launcher_android.cc
+++ b/content/browser/android/child_process_launcher_android.cc
@@ -21,7 +21,6 @@
#include "content/browser/media/android/media_web_contents_observer_android.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/public/browser/browser_thread.h"
-#include "content/public/browser/render_process_host.h"
#include "content/public/common/content_switches.h"
#include "gpu/ipc/common/gpu_surface_tracker.h"
#include "jni/ChildProcessLauncher_jni.h"
@@ -104,15 +103,17 @@
jint handle) {
StartChildProcessCallback* callback =
reinterpret_cast<StartChildProcessCallback*>(client_context);
- if (handle)
- callback->Run(static_cast<base::ProcessHandle>(handle));
+ int launch_result = (handle == base::kNullProcessHandle)
+ ? LAUNCH_RESULT_FAILURE
+ : LAUNCH_RESULT_SUCCESS;
+ callback->Run(static_cast<base::ProcessHandle>(handle), launch_result);
delete callback;
}
void StartChildProcess(
const base::CommandLine::StringVector& argv,
int child_process_id,
- std::unique_ptr<content::FileDescriptorInfo> files_to_register,
+ content::FileDescriptorInfo* files_to_register,
const StartChildProcessCallback& callback) {
JNIEnv* env = AttachCurrentThread();
DCHECK(env);
@@ -133,14 +134,11 @@
int fd = files_to_register->GetFDAt(i);
PCHECK(0 <= fd);
int id = files_to_register->GetIDAt(i);
+ const auto& region = files_to_register->GetRegionAt(i);
bool auto_close = files_to_register->OwnsFD(fd);
- const base::MemoryMappedFile::Region& region =
- files_to_register->GetRegionAt(i);
- int64_t offset = region.offset;
- int64_t size = region.size;
ScopedJavaLocalRef<jobject> j_file_info =
- Java_ChildProcessLauncher_makeFdInfo(env, id, fd, auto_close, offset,
- size);
+ Java_ChildProcessLauncher_makeFdInfo(env, id, fd, auto_close,
+ region.offset, region.size);
PCHECK(j_file_info.obj());
env->SetObjectArrayElement(j_file_infos.obj(), i, j_file_info.obj());
if (auto_close) {
diff --git a/content/browser/android/child_process_launcher_android.h b/content/browser/android/child_process_launcher_android.h
index 78f088a6..2f498ac 100644
--- a/content/browser/android/child_process_launcher_android.h
+++ b/content/browser/android/child_process_launcher_android.h
@@ -7,40 +7,38 @@
#include <jni.h>
-#include <map>
-#include <memory>
-
-#include "base/android/scoped_java_ref.h"
#include "base/callback.h"
#include "base/command_line.h"
-#include "base/files/memory_mapped_file.h"
#include "base/process/process.h"
#include "content/public/browser/file_descriptor_info.h"
-#include "ui/gl/android/scoped_java_surface.h"
+// Contains the methods either being called from or calling to
+// ChildProcessLauncher.java.
namespace content {
-typedef base::Callback<void(base::ProcessHandle)> StartChildProcessCallback;
-// Starts a process as a child process spawned by the Android
-// ActivityManager.
+
+typedef base::Callback<void(base::ProcessHandle, int /* launch result */)>
+ StartChildProcessCallback;
+
+// Starts a process as a child process spawned by the Android ActivityManager.
// The created process handle is returned to the |callback| on success, 0 is
// returned if the process could not be created.
void StartChildProcess(
const base::CommandLine::StringVector& argv,
int child_process_id,
- const std::unique_ptr<FileDescriptorInfo> files_to_register,
+ FileDescriptorInfo* files_to_register,
const StartChildProcessCallback& callback);
-// Stops a child process based on the handle returned form
-// StartChildProcess.
+// Stops a child process based on the handle returned from StartChildProcess.
void StopChildProcess(base::ProcessHandle handle);
bool IsChildProcessOomProtected(base::ProcessHandle handle);
void SetChildProcessInForeground(base::ProcessHandle handle,
bool in_foreground);
+
bool RegisterChildProcessLauncher(JNIEnv* env);
} // namespace content
-#endif // CONTENT_BROWSER_ANDROID_CHILD_PROCESS_LAUNCHER_ANDROID_H_
+#endif // CONTENT_BROWSER_ANDROID_CHILD_PROCESS_LAUNCHER_ANDROID_H_
\ No newline at end of file
diff --git a/content/browser/browser_child_process_host_impl.cc b/content/browser/browser_child_process_host_impl.cc
index 70dc310a..a013741 100644
--- a/content/browser/browser_child_process_host_impl.cc
+++ b/content/browser/browser_child_process_host_impl.cc
@@ -45,6 +45,7 @@
#include "content/public/common/mojo_channel_switches.h"
#include "content/public/common/process_type.h"
#include "content/public/common/result_codes.h"
+#include "content/public/common/sandboxed_process_launcher_delegate.h"
#include "content/public/common/service_manager_connection.h"
#include "mojo/edk/embedder/embedder.h"
#include "services/service_manager/public/cpp/interface_registry.h"
diff --git a/content/browser/child_process_launcher.cc b/content/browser/child_process_launcher.cc
index 51f5696..6970d24 100644
--- a/content/browser/child_process_launcher.cc
+++ b/content/browser/child_process_launcher.cc
@@ -4,386 +4,23 @@
#include "content/browser/child_process_launcher.h"
-#include <memory>
-#include <utility>
-
#include "base/bind.h"
#include "base/command_line.h"
#include "base/files/file_util.h"
#include "base/i18n/icu_util.h"
#include "base/logging.h"
-#include "base/metrics/field_trial.h"
-#include "base/metrics/histogram_macros.h"
#include "base/process/launch.h"
-#include "base/process/process.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/synchronization/lock.h"
-#include "base/threading/thread.h"
#include "build/build_config.h"
-#include "content/public/browser/content_browser_client.h"
-#include "content/public/common/content_descriptors.h"
-#include "content/public/common/content_switches.h"
#include "content/public/common/result_codes.h"
#include "content/public/common/sandboxed_process_launcher_delegate.h"
-#include "mojo/edk/embedder/embedder.h"
-#include "mojo/edk/embedder/named_platform_channel_pair.h"
-#include "mojo/edk/embedder/platform_channel_pair.h"
-#include "mojo/edk/embedder/scoped_platform_handle.h"
-#include "ppapi/features/features.h"
-
-#if defined(OS_WIN)
-#include "base/files/file_path.h"
-#include "base/win/scoped_handle.h"
-#include "base/win/win_util.h"
-#include "content/common/sandbox_win.h"
-#include "content/public/common/sandbox_init.h"
-#include "sandbox/win/src/sandbox_types.h"
-#elif defined(OS_MACOSX)
-#include "content/browser/bootstrap_sandbox_manager_mac.h"
-#include "content/browser/mach_broker_mac.h"
-#include "sandbox/mac/bootstrap_sandbox.h"
-#include "sandbox/mac/pre_exec_delegate.h"
-#elif defined(OS_ANDROID)
-#include "base/android/jni_android.h"
-#include "content/browser/android/child_process_launcher_android.h"
-#elif defined(OS_POSIX)
-#include "base/memory/singleton.h"
-#include "content/browser/renderer_host/render_sandbox_host_linux.h"
-#include "content/browser/zygote_host/zygote_communication_linux.h"
-#include "content/browser/zygote_host/zygote_host_impl_linux.h"
-#include "content/common/child_process_sandbox_support_impl_linux.h"
-#include "content/public/browser/zygote_handle_linux.h"
-#endif
-
-#if defined(OS_POSIX)
-#include "base/posix/global_descriptors.h"
-#include "content/browser/file_descriptor_info_impl.h"
-#include "gin/v8_initializer.h"
-#endif
namespace content {
-namespace {
-
-typedef base::Callback<void(ZygoteHandle,
-#if defined(OS_ANDROID)
- base::ScopedFD,
-#endif
- base::Process,
- int)>
- NotifyCallback;
-
-void RecordHistogramsOnLauncherThread(base::TimeDelta launch_time) {
- DCHECK_CURRENTLY_ON(BrowserThread::PROCESS_LAUNCHER);
- // Log the launch time, separating out the first one (which will likely be
- // slower due to the rest of the browser initializing at the same time).
- static bool done_first_launch = false;
- if (done_first_launch) {
- UMA_HISTOGRAM_TIMES("MPArch.ChildProcessLaunchSubsequent", launch_time);
- } else {
- UMA_HISTOGRAM_TIMES("MPArch.ChildProcessLaunchFirst", launch_time);
- done_first_launch = true;
- }
-}
-
-#if defined(OS_ANDROID)
-// TODO(sievers): Remove this by defining better what happens on what
-// thread in the corresponding Java code.
-void OnChildProcessStartedAndroid(const NotifyCallback& callback,
- BrowserThread::ID client_thread_id,
- const base::TimeTicks begin_launch_time,
- base::ScopedFD mojo_fd,
- base::ProcessHandle handle) {
- int launch_result = (handle == base::kNullProcessHandle)
- ? LAUNCH_RESULT_FAILURE
- : LAUNCH_RESULT_SUCCESS;
- // This can be called on the launcher thread or UI thread.
- base::TimeDelta launch_time = base::TimeTicks::Now() - begin_launch_time;
- BrowserThread::PostTask(
- BrowserThread::PROCESS_LAUNCHER, FROM_HERE,
- base::Bind(&RecordHistogramsOnLauncherThread, launch_time));
-
- base::Closure callback_on_client_thread(
- base::Bind(callback, nullptr, base::Passed(&mojo_fd),
- base::Passed(base::Process(handle)), launch_result));
- if (BrowserThread::CurrentlyOn(client_thread_id)) {
- callback_on_client_thread.Run();
- } else {
- BrowserThread::PostTask(
- client_thread_id, FROM_HERE, callback_on_client_thread);
- }
-}
-#endif
-
-void LaunchOnLauncherThread(
- const NotifyCallback& callback,
- BrowserThread::ID client_thread_id,
- int child_process_id,
- std::unique_ptr<SandboxedProcessLauncherDelegate> delegate,
- mojo::edk::ScopedPlatformHandle client_handle,
- std::unique_ptr<base::CommandLine> cmd_line) {
- DCHECK_CURRENTLY_ON(BrowserThread::PROCESS_LAUNCHER);
-#if !defined(OS_ANDROID)
- ZygoteHandle zygote = nullptr;
- int launch_result = LAUNCH_RESULT_FAILURE;
-#endif
-#if defined(OS_WIN)
- bool launch_elevated = delegate->ShouldLaunchElevated();
-#elif defined(OS_MACOSX)
- base::EnvironmentMap env = delegate->GetEnvironment();
-#elif defined(OS_POSIX) && !defined(OS_ANDROID)
- base::EnvironmentMap env = delegate->GetEnvironment();
-#endif
- base::TimeTicks begin_launch_time = base::TimeTicks::Now();
-
- base::Process process;
-#if defined(OS_WIN)
- if (launch_elevated) {
- // When establishing a Mojo connection, the pipe path has already been added
- // to the command line.
- base::LaunchOptions options;
- options.start_hidden = true;
- process = base::LaunchElevatedProcess(*cmd_line, options);
- } else {
- base::HandlesToInheritVector handles;
- handles.push_back(client_handle.get().handle);
- base::FieldTrialList::AppendFieldTrialHandleIfNeeded(&handles);
- cmd_line->AppendSwitchASCII(
- mojo::edk::PlatformChannelPair::kMojoPlatformChannelHandleSwitch,
- base::UintToString(base::win::HandleToUint32(handles[0])));
- launch_result = StartSandboxedProcess(
- delegate.get(), cmd_line.get(), handles, &process);
- }
-#elif defined(OS_POSIX)
- std::string process_type =
- cmd_line->GetSwitchValueASCII(switches::kProcessType);
- std::unique_ptr<FileDescriptorInfo> files_to_register(
- FileDescriptorInfoImpl::Create());
-
- base::ScopedFD mojo_fd(client_handle.release().handle);
- DCHECK(mojo_fd.is_valid());
-
- int field_trial_handle = base::FieldTrialList::GetFieldTrialHandle();
- if (field_trial_handle != base::kInvalidPlatformFile)
- files_to_register->Share(kFieldTrialDescriptor, field_trial_handle);
-#if defined(OS_ANDROID)
- files_to_register->Share(kMojoIPCChannel, mojo_fd.get());
-#else
- files_to_register->Transfer(kMojoIPCChannel, std::move(mojo_fd));
-#endif
-#endif
-
-#if defined(OS_POSIX) && !defined(OS_MACOSX)
- GetContentClient()->browser()->GetAdditionalMappedFilesForChildProcess(
- *cmd_line, child_process_id, files_to_register.get());
-#if defined(V8_USE_EXTERNAL_STARTUP_DATA)
- bool snapshot_loaded = false;
- base::MemoryMappedFile::Region region;
-#if defined(OS_ANDROID)
- auto maybe_register = [®ion, &files_to_register](int key, int fd) {
- if (fd != -1)
- files_to_register->ShareWithRegion(key, fd, region);
- };
- maybe_register(
- kV8NativesDataDescriptor,
- gin::V8Initializer::GetOpenNativesFileForChildProcesses(®ion));
- maybe_register(
- kV8SnapshotDataDescriptor32,
- gin::V8Initializer::GetOpenSnapshotFileForChildProcesses(®ion, true));
- maybe_register(
- kV8SnapshotDataDescriptor64,
- gin::V8Initializer::GetOpenSnapshotFileForChildProcesses(®ion, false));
-
- snapshot_loaded = true;
-#else
- base::PlatformFile natives_pf =
- gin::V8Initializer::GetOpenNativesFileForChildProcesses(®ion);
- DCHECK_GE(natives_pf, 0);
- files_to_register->ShareWithRegion(
- kV8NativesDataDescriptor, natives_pf, region);
-
- base::PlatformFile snapshot_pf =
- gin::V8Initializer::GetOpenSnapshotFileForChildProcesses(®ion);
- // Failure to load the V8 snapshot is not necessarily an error. V8 can start
- // up (slower) without the snapshot.
- if (snapshot_pf != -1) {
- snapshot_loaded = true;
- files_to_register->ShareWithRegion(
- kV8SnapshotDataDescriptor, snapshot_pf, region);
- }
-#endif
-
- if (process_type != switches::kZygoteProcess) {
- cmd_line->AppendSwitch(::switches::kV8NativesPassedByFD);
- if (snapshot_loaded) {
- cmd_line->AppendSwitch(::switches::kV8SnapshotPassedByFD);
- }
- }
-#endif // defined(V8_USE_EXTERNAL_STARTUP_DATA)
-#endif // defined(OS_POSIX) && !defined(OS_MACOSX)
-
-#if defined(OS_ANDROID)
-#if ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE
- base::MemoryMappedFile::Region icu_region;
- base::PlatformFile icu_pf = base::i18n::GetIcuDataFileHandle(&icu_region);
- files_to_register->ShareWithRegion(
- kAndroidICUDataDescriptor, icu_pf, icu_region);
-#endif // ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE
-
- // Android WebView runs in single process, ensure that we never get here
- // when running in single process mode.
- CHECK(!cmd_line->HasSwitch(switches::kSingleProcess));
-
- StartChildProcess(
- cmd_line->argv(), child_process_id, std::move(files_to_register),
- base::Bind(&OnChildProcessStartedAndroid, callback, client_thread_id,
- begin_launch_time, base::Passed(&mojo_fd)));
-
-#elif defined(OS_POSIX)
- // We need to close the client end of the IPC channel to reliably detect
- // child termination.
-
-#if !defined(OS_MACOSX)
- ZygoteHandle* zygote_handle =
- !base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kNoZygote)
- ? delegate->GetZygote()
- : nullptr;
- // If |zygote_handle| is null, a zygote should not be used.
- if (zygote_handle) {
- // This code runs on the PROCESS_LAUNCHER thread so race conditions are not
- // an issue with the lazy initialization.
- if (*zygote_handle == nullptr) {
- *zygote_handle = CreateZygote();
- }
- zygote = *zygote_handle;
- base::ProcessHandle handle = zygote->ForkRequest(
- cmd_line->argv(), std::move(files_to_register), process_type);
- process = base::Process(handle);
- } else
- // Fall through to the normal posix case below when we're not zygoting.
-#endif // !defined(OS_MACOSX)
- {
- // Convert FD mapping to FileHandleMappingVector
- base::FileHandleMappingVector fds_to_map =
- files_to_register->GetMappingWithIDAdjustment(
- base::GlobalDescriptors::kBaseDescriptor);
-
-#if !defined(OS_MACOSX)
- if (process_type == switches::kRendererProcess) {
- const int sandbox_fd =
- RenderSandboxHostLinux::GetInstance()->GetRendererSocket();
- fds_to_map.push_back(std::make_pair(
- sandbox_fd,
- GetSandboxFD()));
- }
-#endif // defined(OS_MACOSX)
-
- // Actually launch the app.
- base::LaunchOptions options;
- options.environ = env;
- options.fds_to_remap = &fds_to_map;
-
-#if defined(OS_MACOSX)
- // Hold the MachBroker lock for the duration of LaunchProcess. The child
- // will send its task port to the parent almost immediately after startup.
- // The Mach message will be delivered to the parent, but updating the
- // record of the launch will wait until after the placeholder PID is
- // inserted below. This ensures that while the child process may send its
- // port to the parent prior to the parent leaving LaunchProcess, the
- // order in which the record in MachBroker is updated is correct.
- MachBroker* broker = MachBroker::GetInstance();
- broker->GetLock().Acquire();
-
- // Make sure the MachBroker is running, and inform it to expect a
- // check-in from the new process.
- broker->EnsureRunning();
-
- const SandboxType sandbox_type = delegate->GetSandboxType();
- std::unique_ptr<sandbox::PreExecDelegate> pre_exec_delegate;
- if (BootstrapSandboxManager::ShouldEnable()) {
- BootstrapSandboxManager* sandbox_manager =
- BootstrapSandboxManager::GetInstance();
- if (sandbox_manager->EnabledForSandbox(sandbox_type)) {
- pre_exec_delegate = sandbox_manager->sandbox()->NewClient(sandbox_type);
- }
- }
- options.pre_exec_delegate = pre_exec_delegate.get();
-#endif // defined(OS_MACOSX)
-
- process = base::LaunchProcess(*cmd_line, options);
-
-#if defined(OS_MACOSX)
- if (process.IsValid()) {
- broker->AddPlaceholderForPid(process.Pid(), child_process_id);
- } else {
- if (pre_exec_delegate) {
- BootstrapSandboxManager::GetInstance()->sandbox()->RevokeToken(
- pre_exec_delegate->sandbox_token());
- }
- }
-
- // After updating the broker, release the lock and let the child's
- // message be processed on the broker's thread.
- broker->GetLock().Release();
-#endif // defined(OS_MACOSX)
- }
-#endif // else defined(OS_POSIX)
-#if !defined(OS_ANDROID)
- if (process.IsValid()) {
- launch_result = LAUNCH_RESULT_SUCCESS;
- RecordHistogramsOnLauncherThread(base::TimeTicks::Now() -
- begin_launch_time);
- }
- BrowserThread::PostTask(client_thread_id, FROM_HERE,
- base::Bind(callback, zygote, base::Passed(&process),
- launch_result));
-#endif // !defined(OS_ANDROID)
-}
-
-void TerminateOnLauncherThread(ZygoteHandle zygote, base::Process process) {
- DCHECK_CURRENTLY_ON(BrowserThread::PROCESS_LAUNCHER);
-#if defined(OS_ANDROID)
- VLOG(1) << "ChromeProcess: Stopping process with handle "
- << process.Handle();
- StopChildProcess(process.Handle());
-#else
- // Client has gone away, so just kill the process. Using exit code 0
- // means that UMA won't treat this as a crash.
- process.Terminate(RESULT_CODE_NORMAL_EXIT, false);
- // On POSIX, we must additionally reap the child.
-#if defined(OS_POSIX)
-#if !defined(OS_MACOSX)
- if (zygote) {
- // If the renderer was created via a zygote, we have to proxy the reaping
- // through the zygote process.
- zygote->EnsureProcessTerminated(process.Handle());
- } else
-#endif // !OS_MACOSX
- base::EnsureProcessTerminated(std::move(process));
-#endif // OS_POSIX
-#endif // defined(OS_ANDROID)
-}
-
-void SetProcessBackgroundedOnLauncherThread(base::Process process,
- bool background) {
- DCHECK_CURRENTLY_ON(BrowserThread::PROCESS_LAUNCHER);
- if (process.CanBackgroundProcesses()) {
-#if defined(OS_MACOSX)
- process.SetProcessBackgrounded(MachBroker::GetInstance(), background);
-#else
- process.SetProcessBackgrounded(background);
-#endif // defined(OS_MACOSX)
- }
-#if defined(OS_ANDROID)
- SetChildProcessInForeground(process.Handle(), !background);
-#endif
-}
-
-} // namespace
+using internal::ChildProcessLauncherHelper;
ChildProcessLauncher::ChildProcessLauncher(
std::unique_ptr<SandboxedProcessLauncherDelegate> delegate,
- std::unique_ptr<base::CommandLine> cmd_line,
+ std::unique_ptr<base::CommandLine> command_line,
int child_process_id,
Client* client,
const std::string& mojo_child_token,
@@ -392,7 +29,6 @@
: client_(client),
termination_status_(base::TERMINATION_STATUS_NORMAL_TERMINATION),
exit_code_(RESULT_CODE_NORMAL_EXIT),
- zygote_(nullptr),
starting_(true),
process_error_callback_(process_error_callback),
#if defined(ADDRESS_SANITIZER) || defined(LEAK_SANITIZER) || \
@@ -406,148 +42,48 @@
weak_factory_(this) {
DCHECK(CalledOnValidThread());
CHECK(BrowserThread::GetCurrentThreadIdentifier(&client_thread_id_));
- Launch(std::move(delegate), std::move(cmd_line), child_process_id);
+
+ helper_ = new ChildProcessLauncherHelper(
+ child_process_id, client_thread_id_,
+ std::move(command_line), std::move(delegate),
+ weak_factory_.GetWeakPtr(), terminate_on_shutdown);
+ helper_->StartLaunchOnClientThread();
}
ChildProcessLauncher::~ChildProcessLauncher() {
DCHECK(CalledOnValidThread());
- if (process_.IsValid() && terminate_child_on_shutdown_) {
- // On Posix, EnsureProcessTerminated can lead to 2 seconds of sleep! So
- // don't this on the UI/IO threads.
- BrowserThread::PostTask(BrowserThread::PROCESS_LAUNCHER, FROM_HERE,
- base::Bind(&TerminateOnLauncherThread, zygote_,
- base::Passed(&process_)));
- }
-}
-
-void ChildProcessLauncher::Launch(
- std::unique_ptr<SandboxedProcessLauncherDelegate> delegate,
- std::unique_ptr<base::CommandLine> cmd_line,
- int child_process_id) {
- DCHECK(CalledOnValidThread());
-
-#if defined(OS_ANDROID)
- // Android only supports renderer, sandboxed utility and gpu.
- std::string process_type =
- cmd_line->GetSwitchValueASCII(switches::kProcessType);
- CHECK(process_type == switches::kGpuProcess ||
- process_type == switches::kRendererProcess ||
-#if BUILDFLAG(ENABLE_PLUGINS)
- process_type == switches::kPpapiPluginProcess ||
-#endif
- process_type == switches::kUtilityProcess)
- << "Unsupported process type: " << process_type;
-
- // Non-sandboxed utility or renderer process are currently not supported.
- DCHECK(process_type == switches::kGpuProcess ||
- !cmd_line->HasSwitch(switches::kNoSandbox));
-
-#endif
- mojo::edk::ScopedPlatformHandle server_handle;
- mojo::edk::ScopedPlatformHandle client_handle;
-#if defined(OS_WIN)
- if (delegate->ShouldLaunchElevated()) {
- mojo::edk::NamedPlatformChannelPair named_pair;
- server_handle = named_pair.PassServerHandle();
- named_pair.PrepareToPassClientHandleToChildProcess(cmd_line.get());
- } else
-#endif
- {
- mojo::edk::PlatformChannelPair channel_pair;
- server_handle = channel_pair.PassServerHandle();
- client_handle = channel_pair.PassClientHandle();
- }
- NotifyCallback reply_callback(base::Bind(&ChildProcessLauncher::DidLaunch,
- weak_factory_.GetWeakPtr(),
- terminate_child_on_shutdown_,
- base::Passed(&server_handle)));
- BrowserThread::PostTask(
- BrowserThread::PROCESS_LAUNCHER, FROM_HERE,
- base::Bind(&LaunchOnLauncherThread, reply_callback, client_thread_id_,
- child_process_id, base::Passed(&delegate),
- base::Passed(&client_handle), base::Passed(&cmd_line)));
-}
-
-void ChildProcessLauncher::UpdateTerminationStatus(bool known_dead) {
- DCHECK(CalledOnValidThread());
-#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
- if (zygote_) {
- termination_status_ = zygote_->GetTerminationStatus(
- process_.Handle(), known_dead, &exit_code_);
- } else if (known_dead) {
- termination_status_ =
- base::GetKnownDeadTerminationStatus(process_.Handle(), &exit_code_);
- } else {
-#elif defined(OS_MACOSX)
- if (known_dead) {
- termination_status_ =
- base::GetKnownDeadTerminationStatus(process_.Handle(), &exit_code_);
- } else {
-#elif defined(OS_ANDROID)
- if (IsChildProcessOomProtected(process_.Handle())) {
- termination_status_ = base::TERMINATION_STATUS_OOM_PROTECTED;
- } else {
-#else
- {
-#endif
- termination_status_ =
- base::GetTerminationStatus(process_.Handle(), &exit_code_);
+ if (process_.process.IsValid() && terminate_child_on_shutdown_) {
+ // Client has gone away, so just kill the process.
+ ChildProcessLauncherHelper::ForceNormalProcessTerminationAsync(
+ std::move(process_));
}
}
void ChildProcessLauncher::SetProcessBackgrounded(bool background) {
DCHECK(CalledOnValidThread());
- base::Process to_pass = process_.Duplicate();
- BrowserThread::PostTask(BrowserThread::PROCESS_LAUNCHER, FROM_HERE,
- base::Bind(&SetProcessBackgroundedOnLauncherThread,
- base::Passed(&to_pass), background));
+ base::Process to_pass = process_.process.Duplicate();
+ BrowserThread::PostTask(
+ BrowserThread::PROCESS_LAUNCHER, FROM_HERE,
+ base::Bind(
+ &ChildProcessLauncherHelper::SetProcessBackgroundedOnLauncherThread,
+ base::Passed(&to_pass),
+ background));
}
-void ChildProcessLauncher::DidLaunch(
- base::WeakPtr<ChildProcessLauncher> instance,
- bool terminate_on_shutdown,
+void ChildProcessLauncher::Notify(
+ ChildProcessLauncherHelper::Process process,
mojo::edk::ScopedPlatformHandle server_handle,
- ZygoteHandle zygote,
-#if defined(OS_ANDROID)
- base::ScopedFD mojo_fd,
-#endif
- base::Process process,
int error_code) {
- if (!process.IsValid())
- LOG(ERROR) << "Failed to launch child process";
-
- if (instance.get()) {
- instance->Notify(zygote, std::move(server_handle),
- std::move(process), error_code);
- } else {
- if (process.IsValid() && terminate_on_shutdown) {
- // On Posix, EnsureProcessTerminated can lead to 2 seconds of sleep! So
- // don't this on the UI/IO threads.
- BrowserThread::PostTask(BrowserThread::PROCESS_LAUNCHER, FROM_HERE,
- base::Bind(&TerminateOnLauncherThread, zygote,
- base::Passed(&process)));
- }
- }
-}
-
-void ChildProcessLauncher::Notify(ZygoteHandle zygote,
- mojo::edk::ScopedPlatformHandle server_handle,
- base::Process process,
- int error_code) {
DCHECK(CalledOnValidThread());
starting_ = false;
process_ = std::move(process);
- if (process_.IsValid()) {
+ if (process_.process.IsValid()) {
// Set up Mojo IPC to the new process.
- mojo::edk::ChildProcessLaunched(process_.Handle(), std::move(server_handle),
- mojo_child_token_, process_error_callback_);
- }
-
-#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
- zygote_ = zygote;
-#endif
- if (process_.IsValid()) {
+ mojo::edk::ChildProcessLaunched(process_.process.Handle(),
+ std::move(server_handle),
+ mojo_child_token_,
+ process_error_callback_);
client_->OnProcessLaunched();
} else {
mojo::edk::ChildProcessLaunchFailed(mojo_child_token_);
@@ -565,36 +101,50 @@
const base::Process& ChildProcessLauncher::GetProcess() const {
// TODO(crbug.com/469248): This fails in some tests.
// DCHECK(CalledOnValidThread());
- return process_;
+ return process_.process;
}
base::TerminationStatus ChildProcessLauncher::GetChildTerminationStatus(
bool known_dead,
int* exit_code) {
DCHECK(CalledOnValidThread());
- if (!process_.IsValid()) {
+ if (!process_.process.IsValid()) {
// Process is already gone, so return the cached termination status.
if (exit_code)
*exit_code = exit_code_;
return termination_status_;
}
- UpdateTerminationStatus(known_dead);
+ termination_status_ = ChildProcessLauncherHelper::GetTerminationStatus(
+ process_, known_dead, &exit_code_);
if (exit_code)
*exit_code = exit_code_;
- // POSIX: If the process crashed, then the kernel closed the socket
- // for it and so the child has already died by the time we get
- // here. Since GetTerminationStatus called waitpid with WNOHANG,
- // it'll reap the process. However, if GetTerminationStatus didn't
- // reap the child (because it was still running), we'll need to
- // Terminate via ProcessWatcher. So we can't close the handle here.
+ // POSIX: If the process crashed, then the kernel closed the socket for it and
+ // so the child has already died by the time we get here. Since
+ // GetTerminationStatus called waitpid with WNOHANG, it'll reap the process.
+ // However, if GetTerminationStatus didn't reap the child (because it was
+ // still running), we'll need to Terminate via ProcessWatcher. So we can't
+ // close the handle here.
if (termination_status_ != base::TERMINATION_STATUS_STILL_RUNNING)
- process_.Close();
+ process_.process.Close();
return termination_status_;
}
+bool ChildProcessLauncher::Terminate(int exit_code, bool wait) {
+ return IsStarting() ? false
+ : ChildProcessLauncherHelper::TerminateProcess(
+ GetProcess(), exit_code, wait);
+}
+
+// static
+bool ChildProcessLauncher::TerminateProcess(const base::Process& process,
+ int exit_code,
+ bool wait) {
+ return ChildProcessLauncherHelper::TerminateProcess(process, exit_code, wait);
+}
+
ChildProcessLauncher::Client* ChildProcessLauncher::ReplaceClientForTest(
Client* client) {
Client* ret = client_;
diff --git a/content/browser/child_process_launcher.h b/content/browser/child_process_launcher.h
index 8e1e1b3..6a17aa02 100644
--- a/content/browser/child_process_launcher.h
+++ b/content/browser/child_process_launcher.h
@@ -5,31 +5,31 @@
#ifndef CONTENT_BROWSER_CHILD_PROCESS_LAUNCHER_H_
#define CONTENT_BROWSER_CHILD_PROCESS_LAUNCHER_H_
-#include "base/files/scoped_file.h"
+#include <memory>
+
#include "base/macros.h"
+#include "base/memory/ref_counted.h"
#include "base/memory/shared_memory.h"
#include "base/memory/weak_ptr.h"
#include "base/process/kill.h"
-#include "base/process/launch.h"
#include "base/process/process.h"
#include "base/threading/non_thread_safe.h"
#include "build/build_config.h"
+#include "content/browser/child_process_launcher_helper.h"
#include "content/common/content_export.h"
#include "content/public/browser/browser_thread.h"
-#include "content/public/common/sandboxed_process_launcher_delegate.h"
+#include "content/public/common/result_codes.h"
#include "mojo/edk/embedder/embedder.h"
#include "mojo/edk/embedder/scoped_platform_handle.h"
-#if defined(OS_WIN)
-#include "sandbox/win/src/sandbox_types.h"
-#endif
-
namespace base {
class CommandLine;
}
namespace content {
+class SandboxedProcessLauncherDelegate;
+
// Note: These codes are listed in a histogram and any new codes should be added
// at the end.
enum LaunchResultCode {
@@ -109,44 +109,42 @@
// this after the process has started.
void SetProcessBackgrounded(bool background);
+ // Terminates the process associated with this ChildProcessLauncher.
+ // Returns true if the process was stopped, false if the process had not been
+ // started yet or could not be stopped.
+ // Note that |exit_code| and |wait| are not used on Android.
+ bool Terminate(int exit_code, bool wait);
+
+ // Similar to Terminate() but takes in a |process|.
+ // On Android |process| must have been started by ChildProcessLauncher for
+ // this method to work.
+ static bool TerminateProcess(const base::Process& process,
+ int exit_code,
+ bool wait);
+
// Replaces the ChildProcessLauncher::Client for testing purposes. Returns the
// previous client.
Client* ReplaceClientForTest(Client* client);
private:
- // Posts a task to the launcher thread to do the actual work.
- void Launch(std::unique_ptr<SandboxedProcessLauncherDelegate> delegate,
- std::unique_ptr<base::CommandLine> cmd_line,
- int child_process_id);
+ friend class internal::ChildProcessLauncherHelper;
void UpdateTerminationStatus(bool known_dead);
- // This is always called on the client thread after an attempt
- // to launch the child process on the launcher thread.
- // It makes sure we always perform the necessary cleanup if the
- // client went away.
- static void DidLaunch(base::WeakPtr<ChildProcessLauncher> instance,
- bool terminate_on_shutdown,
- mojo::edk::ScopedPlatformHandle server_handle,
- ZygoteHandle zygote,
-#if defined(OS_ANDROID)
- base::ScopedFD mojo_fd,
-#endif
- base::Process process,
- int error_code);
-
// Notifies the client about the result of the operation.
- void Notify(ZygoteHandle zygote,
+ void Notify(internal::ChildProcessLauncherHelper::Process process,
mojo::edk::ScopedPlatformHandle server_handle,
- base::Process process,
int error_code);
Client* client_;
BrowserThread::ID client_thread_id_;
- base::Process process_;
+
+ // The process associated with this ChildProcessLauncher. Set in Notify by
+ // ChildProcessLauncherHelper once the process was started.
+ internal::ChildProcessLauncherHelper::Process process_;
+
base::TerminationStatus termination_status_;
int exit_code_;
- ZygoteHandle zygote_;
bool starting_;
const mojo::edk::ProcessErrorCallback process_error_callback_;
@@ -156,6 +154,8 @@
const std::string mojo_child_token_;
+ scoped_refptr<internal::ChildProcessLauncherHelper> helper_;
+
base::WeakPtrFactory<ChildProcessLauncher> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(ChildProcessLauncher);
diff --git a/content/browser/child_process_launcher_helper.cc b/content/browser/child_process_launcher_helper.cc
new file mode 100644
index 0000000..9aa83459
--- /dev/null
+++ b/content/browser/child_process_launcher_helper.cc
@@ -0,0 +1,165 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/child_process_launcher_helper.h"
+
+#include "base/metrics/histogram_macros.h"
+#include "content/browser/child_process_launcher.h"
+#include "content/public/common/content_switches.h"
+#include "content/public/common/sandboxed_process_launcher_delegate.h"
+#include "mojo/edk/embedder/platform_channel_pair.h"
+
+namespace content {
+namespace internal {
+
+namespace {
+
+void RecordHistogramsOnLauncherThread(base::TimeDelta launch_time) {
+ DCHECK_CURRENTLY_ON(BrowserThread::PROCESS_LAUNCHER);
+ // Log the launch time, separating out the first one (which will likely be
+ // slower due to the rest of the browser initializing at the same time).
+ static bool done_first_launch = false;
+ if (done_first_launch) {
+ UMA_HISTOGRAM_TIMES("MPArch.ChildProcessLaunchSubsequent", launch_time);
+ } else {
+ UMA_HISTOGRAM_TIMES("MPArch.ChildProcessLaunchFirst", launch_time);
+ done_first_launch = true;
+ }
+}
+
+} // namespace
+
+ChildProcessLauncherHelper::Process::Process(Process&& other)
+ : process(std::move(other.process))
+#if defined(OS_LINUX)
+ , zygote(other.zygote)
+#endif
+{
+}
+
+ChildProcessLauncherHelper::Process&
+ChildProcessLauncherHelper::Process::Process::operator=(
+ ChildProcessLauncherHelper::Process&& other) {
+ DCHECK_NE(this, &other);
+ process = std::move(other.process);
+#if defined(OS_LINUX)
+ zygote = other.zygote;
+#endif
+ return *this;
+}
+
+ChildProcessLauncherHelper::ChildProcessLauncherHelper(
+ int child_process_id,
+ BrowserThread::ID client_thread_id,
+ std::unique_ptr<base::CommandLine> command_line,
+ std::unique_ptr<SandboxedProcessLauncherDelegate> delegate,
+ const base::WeakPtr<ChildProcessLauncher>& child_process_launcher,
+ bool terminate_on_shutdown)
+ : child_process_id_(child_process_id),
+ client_thread_id_(client_thread_id),
+ command_line_(std::move(command_line)),
+ delegate_(std::move(delegate)),
+ child_process_launcher_(child_process_launcher),
+ terminate_on_shutdown_(terminate_on_shutdown) {
+}
+
+ChildProcessLauncherHelper::~ChildProcessLauncherHelper() {
+}
+
+void ChildProcessLauncherHelper::StartLaunchOnClientThread() {
+ DCHECK_CURRENTLY_ON(client_thread_id_);
+
+ BeforeLaunchOnClientThread();
+
+ mojo_server_handle_ = PrepareMojoPipeHandlesOnClientThread();
+ if (!mojo_server_handle_.is_valid()) {
+ mojo::edk::PlatformChannelPair channel_pair;
+ mojo_server_handle_ = channel_pair.PassServerHandle();
+ mojo_client_handle_ = channel_pair.PassClientHandle();
+ }
+
+ BrowserThread::PostTask(
+ BrowserThread::PROCESS_LAUNCHER, FROM_HERE,
+ base::Bind(&ChildProcessLauncherHelper::LaunchOnLauncherThread, this));
+}
+
+void ChildProcessLauncherHelper::LaunchOnLauncherThread() {
+ DCHECK_CURRENTLY_ON(BrowserThread::PROCESS_LAUNCHER);
+
+ begin_launch_time_ = base::TimeTicks::Now();
+
+ std::unique_ptr<FileMappedForLaunch> files_to_register = GetFilesToMap();
+
+ bool is_synchronous_launch = true;
+ int launch_result = LAUNCH_RESULT_FAILURE;
+ base::LaunchOptions options;
+ BeforeLaunchOnLauncherThread(*files_to_register, &options);
+
+ Process process = LaunchProcessOnLauncherThread(options,
+ std::move(files_to_register),
+ &is_synchronous_launch,
+ &launch_result);
+
+ AfterLaunchOnLauncherThread(process, options);
+
+ if (is_synchronous_launch) {
+ PostLaunchOnLauncherThread(std::move(process), launch_result, false);
+ }
+}
+
+void ChildProcessLauncherHelper::PostLaunchOnLauncherThread(
+ ChildProcessLauncherHelper::Process process,
+ int launch_result,
+ bool post_launch_on_client_thread_called) {
+ // Release the client handle now that the process has been started (the pipe
+ // may not signal when the process dies otherwise and we would not detect the
+ // child process died).
+ mojo_client_handle_.reset();
+
+ if (process.process.IsValid()) {
+ RecordHistogramsOnLauncherThread(
+ base::TimeTicks::Now() - begin_launch_time_);
+ }
+
+ if (!post_launch_on_client_thread_called) {
+ BrowserThread::PostTask(
+ client_thread_id_, FROM_HERE,
+ base::Bind(&ChildProcessLauncherHelper::PostLaunchOnClientThread,
+ this, base::Passed(&process), launch_result));
+ }
+}
+
+void ChildProcessLauncherHelper::PostLaunchOnClientThread(
+ ChildProcessLauncherHelper::Process process,
+ int error_code) {
+ if (child_process_launcher_) {
+ child_process_launcher_->Notify(
+ std::move(process), std::move(mojo_server_handle_), error_code);
+ } else if (process.process.IsValid() && terminate_on_shutdown_) {
+ // Client is gone, terminate the process.
+ ForceNormalProcessTerminationAsync(std::move(process));
+ }
+}
+
+std::string ChildProcessLauncherHelper::GetProcessType() {
+ return command_line()->GetSwitchValueASCII(switches::kProcessType);
+}
+
+// static
+void ChildProcessLauncherHelper::ForceNormalProcessTerminationAsync(
+ ChildProcessLauncherHelper::Process process) {
+ if (BrowserThread::CurrentlyOn(BrowserThread::PROCESS_LAUNCHER)) {
+ ForceNormalProcessTerminationSync(std::move(process));
+ return;
+ }
+ // On Posix, EnsureProcessTerminated can lead to 2 seconds of sleep!
+ // So don't do this on the UI/IO threads.
+ BrowserThread::PostTask(
+ BrowserThread::PROCESS_LAUNCHER, FROM_HERE,
+ base::Bind(&ChildProcessLauncherHelper::ForceNormalProcessTerminationSync,
+ base::Passed(&process)));
+}
+
+} // namespace internal
+} // namespace content
diff --git a/content/browser/child_process_launcher_helper.h b/content/browser/child_process_launcher_helper.h
new file mode 100644
index 0000000..ead7d44f
--- /dev/null
+++ b/content/browser/child_process_launcher_helper.h
@@ -0,0 +1,191 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_CHILD_PROCESS_LAUNCHER_HELPER_H_
+#define CONTENT_BROWSER_CHILD_PROCESS_LAUNCHER_HELPER_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/process/kill.h"
+#include "base/process/process.h"
+#include "build/build_config.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/common/result_codes.h"
+#include "mojo/edk/embedder/embedder.h"
+#include "mojo/edk/embedder/scoped_platform_handle.h"
+
+#if defined(OS_WIN)
+#include "sandbox/win/src/sandbox_types.h"
+#else
+#include "content/public/browser/file_descriptor_info.h"
+#endif
+
+#if defined(OS_LINUX)
+#include "content/public/common/zygote_handle.h"
+#endif
+
+namespace base {
+class CommandLine;
+}
+
+namespace content {
+
+class ChildProcessLauncher;
+class FileDescriptorInfo;
+class SandboxedProcessLauncherDelegate;
+
+namespace internal {
+
+
+#if defined(OS_WIN)
+using FileMappedForLaunch = base::HandlesToInheritVector;
+#else
+using FileMappedForLaunch = FileDescriptorInfo;
+#endif
+
+// ChildProcessLauncherHelper is used by ChildProcessLauncher to start a
+// process. Since ChildProcessLauncher can be deleted by its client at any time,
+// this class is used to keep state as the process is started asynchronously.
+// It also contains the platform specific pieces.
+class ChildProcessLauncherHelper :
+ public base::RefCountedThreadSafe<ChildProcessLauncherHelper> {
+ public:
+ // Abstraction around a process required to deal in a platform independent way
+ // between Linux (which can use zygotes) and the other platforms.
+ struct Process {
+ Process() {}
+ Process(Process&& other);
+ ~Process() {}
+ Process& operator=(Process&& other);
+
+ base::Process process;
+
+#if defined(OS_LINUX)
+ ZygoteHandle zygote = nullptr;
+#endif
+ };
+
+ ChildProcessLauncherHelper(
+ int child_process_id,
+ BrowserThread::ID client_thread_id,
+ std::unique_ptr<base::CommandLine> command_line,
+ std::unique_ptr<SandboxedProcessLauncherDelegate> delegate,
+ const base::WeakPtr<ChildProcessLauncher>& child_process_launcher,
+ bool terminate_on_shutdown);
+
+ // The methods below are defined in the order they are called.
+
+ // Starts the flow of launching the process.
+ void StartLaunchOnClientThread();
+
+ // Platform specific.
+ void BeforeLaunchOnClientThread();
+
+ // Called in to give implementors a chance at creating a server pipe.
+ // Platform specific.
+ mojo::edk::ScopedPlatformHandle PrepareMojoPipeHandlesOnClientThread();
+
+ // Returns the list of files that should be mapped in the child process.
+ // Platform specific.
+ std::unique_ptr<FileMappedForLaunch> GetFilesToMap();
+
+ // Platform specific.
+ void BeforeLaunchOnLauncherThread(
+ const FileMappedForLaunch& files_to_register,
+ base::LaunchOptions* options);
+
+ // Does the actual starting of the process.
+ // |is_synchronous_launch| is set to false if the starting of the process is
+ // asynchonous (this is the case on Android), in which case the returned
+ // Process is not valid (and PostLaunchOnLauncherThread() will provide the
+ // process once it is available).
+ // Platform specific.
+ ChildProcessLauncherHelper::Process LaunchProcessOnLauncherThread(
+ const base::LaunchOptions& options,
+ std::unique_ptr<FileMappedForLaunch> files_to_register,
+ bool* is_synchronous_launch,
+ int* launch_result);
+
+ // Called right after the process has been launched, whether it was created
+ // yet or not.
+ // Platform specific.
+ void AfterLaunchOnLauncherThread(
+ const ChildProcessLauncherHelper::Process& process,
+ const base::LaunchOptions& options);
+
+ // Called once the process has been created, successfully or not.
+ // If |post_launch_on_client_thread_called| is false,
+ // this calls PostLaunchOnClientThread on the client thread.
+ void PostLaunchOnLauncherThread(ChildProcessLauncherHelper::Process process,
+ int launch_result,
+ bool post_launch_on_client_thread_called);
+
+ // Note that this could be called before PostLaunchOnLauncherThread() is
+ // called.
+ void PostLaunchOnClientThread(ChildProcessLauncherHelper::Process process,
+ int error_code);
+
+ int client_thread_id() const { return client_thread_id_; }
+
+ // Returns the termination status and sets |exit_code| if non null.
+ // See ChildProcessLauncher::GetChildTerminationStatus for more info.
+ static base::TerminationStatus GetTerminationStatus(
+ const ChildProcessLauncherHelper::Process& process,
+ bool known_dead,
+ int* exit_code);
+
+ // Terminates |process|.
+ // Returns true if the process was stopped, false if the process had not been
+ // started yet or could not be stopped.
+ // Note that |exit_code| and |wait| are not used on Android.
+ static bool TerminateProcess(const base::Process& process,
+ int exit_code,
+ bool wait);
+
+ // Terminates the process with the normal exit code and ensures it has been
+ // stopped. By returning a normal exit code this ensures UMA won't treat this
+ // as a crash.
+ // Returns immediately and perform the work on the launcher thread.
+ static void ForceNormalProcessTerminationAsync(
+ ChildProcessLauncherHelper::Process process);
+
+ static void SetProcessBackgroundedOnLauncherThread(
+ base::Process process, bool background);
+
+ private:
+ friend class base::RefCountedThreadSafe<ChildProcessLauncherHelper>;
+
+ ~ChildProcessLauncherHelper();
+
+ void LaunchOnLauncherThread();
+
+ const mojo::edk::PlatformHandle& mojo_client_handle() const {
+ return mojo_client_handle_.get();
+ }
+ base::CommandLine* command_line() { return command_line_.get(); }
+ int child_process_id() const { return child_process_id_; }
+
+ std::string GetProcessType();
+
+ static void ForceNormalProcessTerminationSync(
+ ChildProcessLauncherHelper::Process process);
+
+ const int child_process_id_;
+ const BrowserThread::ID client_thread_id_;
+ base::TimeTicks begin_launch_time_;
+ std::unique_ptr<base::CommandLine> command_line_;
+ std::unique_ptr<SandboxedProcessLauncherDelegate> delegate_;
+ base::WeakPtr<ChildProcessLauncher> child_process_launcher_;
+ mojo::edk::ScopedPlatformHandle mojo_client_handle_;
+ mojo::edk::ScopedPlatformHandle mojo_server_handle_;
+ bool terminate_on_shutdown_;
+};
+
+} // namespace internal
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_CHILD_PROCESS_LAUNCHER_HELPER_H_
diff --git a/content/browser/child_process_launcher_helper_android.cc b/content/browser/child_process_launcher_helper_android.cc
new file mode 100644
index 0000000..5a24175
--- /dev/null
+++ b/content/browser/child_process_launcher_helper_android.cc
@@ -0,0 +1,183 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/child_process_launcher_helper.h"
+
+#include <memory>
+
+#include "base/i18n/icu_util.h"
+#include "base/logging.h"
+#include "base/metrics/field_trial.h"
+#include "content/browser/android/child_process_launcher_android.h"
+#include "content/browser/child_process_launcher_helper_posix.h"
+#include "content/browser/file_descriptor_info_impl.h"
+#include "content/browser/web_contents/web_contents_impl.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/render_process_host.h"
+#include "content/public/common/content_descriptors.h"
+#include "content/public/common/content_switches.h"
+#include "gin/v8_initializer.h"
+
+namespace content {
+namespace internal {
+
+namespace {
+
+// Callback invoked from Java once the process has been started.
+void ChildProcessStartedCallback(
+ ChildProcessLauncherHelper* helper,
+ base::ProcessHandle handle,
+ int launch_result) {
+ // TODO(jcivelli): Remove this by defining better what happens on what thread
+ // in the corresponding Java code.
+ ChildProcessLauncherHelper::Process process;
+ process.process = base::Process(handle);
+ if (BrowserThread::CurrentlyOn(BrowserThread::PROCESS_LAUNCHER)) {
+ helper->PostLaunchOnLauncherThread(
+ std::move(process),
+ launch_result,
+ false); // post_launch_on_client_thread_called
+ return;
+ }
+
+ bool on_client_thread = BrowserThread::CurrentlyOn(
+ static_cast<BrowserThread::ID>(helper->client_thread_id()));
+ BrowserThread::PostTask(BrowserThread::PROCESS_LAUNCHER, FROM_HERE,
+ base::Bind(&ChildProcessLauncherHelper::PostLaunchOnLauncherThread,
+ helper,
+ base::Passed(std::move(process)),
+ launch_result,
+ on_client_thread));
+ if (on_client_thread) {
+ ChildProcessLauncherHelper::Process process;
+ process.process = base::Process(handle);
+ helper->PostLaunchOnClientThread(std::move(process), launch_result);
+ }
+}
+
+} // namespace
+
+void ChildProcessLauncherHelper::BeforeLaunchOnClientThread() {
+ // Android only supports renderer, sandboxed utility and gpu.
+ std::string process_type =
+ command_line()->GetSwitchValueASCII(switches::kProcessType);
+ CHECK(process_type == switches::kGpuProcess ||
+ process_type == switches::kRendererProcess ||
+#if BUILDFLAG(ENABLE_PLUGINS)
+ process_type == switches::kPpapiPluginProcess ||
+#endif
+ process_type == switches::kUtilityProcess)
+ << "Unsupported process type: " << process_type;
+
+ // Non-sandboxed utility or renderer process are currently not supported.
+ DCHECK(process_type == switches::kGpuProcess ||
+ !command_line()->HasSwitch(switches::kNoSandbox));
+}
+
+mojo::edk::ScopedPlatformHandle
+ChildProcessLauncherHelper::PrepareMojoPipeHandlesOnClientThread() {
+ return mojo::edk::ScopedPlatformHandle();
+}
+
+std::unique_ptr<FileDescriptorInfo>
+ChildProcessLauncherHelper::GetFilesToMap() {
+ DCHECK_CURRENTLY_ON(BrowserThread::PROCESS_LAUNCHER);
+
+ // Android WebView runs in single process, ensure that we never get here when
+ // running in single process mode.
+ CHECK(!command_line()->HasSwitch(switches::kSingleProcess));
+
+ std::unique_ptr<FileDescriptorInfo> files_to_register =
+ CreateDefaultPosixFilesToMap(*command_line(), child_process_id(),
+ mojo_client_handle());
+
+#if defined(V8_USE_EXTERNAL_STARTUP_DATA)
+ base::MemoryMappedFile::Region region;
+ auto maybe_register = [®ion, &files_to_register](int key, int fd) {
+ if (fd != -1)
+ files_to_register->ShareWithRegion(key, fd, region);
+ };
+ maybe_register(
+ kV8NativesDataDescriptor,
+ gin::V8Initializer::GetOpenNativesFileForChildProcesses(®ion));
+ maybe_register(
+ kV8SnapshotDataDescriptor32,
+ gin::V8Initializer::GetOpenSnapshotFileForChildProcesses(®ion, true));
+ maybe_register(
+ kV8SnapshotDataDescriptor64,
+ gin::V8Initializer::GetOpenSnapshotFileForChildProcesses(®ion, false));
+
+ command_line()->AppendSwitch(::switches::kV8NativesPassedByFD);
+ command_line()->AppendSwitch(::switches::kV8SnapshotPassedByFD);
+#endif // defined(V8_USE_EXTERNAL_STARTUP_DATA)
+
+#if ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE
+ int fd = base::i18n::GetIcuDataFileHandle(®ion);
+ files_to_register->ShareWithRegion(kAndroidICUDataDescriptor, fd, region);
+#endif // ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE
+
+ return files_to_register;
+}
+
+void ChildProcessLauncherHelper::BeforeLaunchOnLauncherThread(
+ const FileDescriptorInfo& files_to_register,
+ base::LaunchOptions* options) {
+}
+
+ChildProcessLauncherHelper::Process
+ChildProcessLauncherHelper::LaunchProcessOnLauncherThread(
+ const base::LaunchOptions& options,
+ std::unique_ptr<FileDescriptorInfo> files_to_register,
+ bool* is_synchronous_launch,
+ int* launch_result) {
+ *is_synchronous_launch = false;
+
+ StartChildProcess(command_line()->argv(),
+ child_process_id(),
+ files_to_register.get(),
+ base::Bind(&ChildProcessStartedCallback,
+ RetainedRef(this)));
+
+ return Process();
+}
+
+void ChildProcessLauncherHelper::AfterLaunchOnLauncherThread(
+ const ChildProcessLauncherHelper::Process& process,
+ const base::LaunchOptions& options) {
+}
+
+// static
+base::TerminationStatus ChildProcessLauncherHelper::GetTerminationStatus(
+ const ChildProcessLauncherHelper::Process& process,
+ bool known_dead,
+ int* exit_code) {
+ if (IsChildProcessOomProtected(process.process.Handle()))
+ return base::TERMINATION_STATUS_OOM_PROTECTED;
+ return base::GetTerminationStatus(process.process.Handle(), exit_code);
+}
+
+// static
+bool ChildProcessLauncherHelper::TerminateProcess(
+ const base::Process& process, int exit_code, bool wait) {
+ StopChildProcess(process.Handle());
+ return true;
+}
+
+// static
+void ChildProcessLauncherHelper::ForceNormalProcessTerminationSync(
+ ChildProcessLauncherHelper::Process process) {
+ DCHECK_CURRENTLY_ON(BrowserThread::PROCESS_LAUNCHER);
+ VLOG(1) << "ChromeProcess: Stopping process with handle "
+ << process.process.Handle();
+ StopChildProcess(process.process.Handle());
+}
+
+// static
+void ChildProcessLauncherHelper::SetProcessBackgroundedOnLauncherThread(
+ base::Process process, bool background) {
+ SetChildProcessInForeground(process.Handle(), !background);
+}
+
+} // namespace internal
+} // namespace content
diff --git a/content/browser/child_process_launcher_helper_linux.cc b/content/browser/child_process_launcher_helper_linux.cc
new file mode 100644
index 0000000..f0a9a73
--- /dev/null
+++ b/content/browser/child_process_launcher_helper_linux.cc
@@ -0,0 +1,176 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/posix/global_descriptors.h"
+#include "content/browser/child_process_launcher.h"
+#include "content/browser/child_process_launcher_helper.h"
+#include "content/browser/child_process_launcher_helper_posix.h"
+#include "content/browser/renderer_host/render_sandbox_host_linux.h"
+#include "content/browser/zygote_host/zygote_communication_linux.h"
+#include "content/browser/zygote_host/zygote_host_impl_linux.h"
+#include "content/common/child_process_sandbox_support_impl_linux.h"
+#include "content/public/browser/content_browser_client.h"
+#include "content/public/browser/zygote_handle_linux.h"
+#include "content/public/common/content_client.h"
+#include "content/public/common/content_switches.h"
+#include "content/public/common/result_codes.h"
+#include "content/public/common/sandboxed_process_launcher_delegate.h"
+#include "gin/v8_initializer.h"
+
+namespace content {
+namespace internal {
+
+mojo::edk::ScopedPlatformHandle
+ChildProcessLauncherHelper::PrepareMojoPipeHandlesOnClientThread() {
+ DCHECK_CURRENTLY_ON(client_thread_id_);
+ return mojo::edk::ScopedPlatformHandle();
+}
+
+void ChildProcessLauncherHelper::BeforeLaunchOnClientThread() {
+ DCHECK_CURRENTLY_ON(client_thread_id_);
+}
+
+std::unique_ptr<FileMappedForLaunch>
+ChildProcessLauncherHelper::GetFilesToMap() {
+ DCHECK_CURRENTLY_ON(BrowserThread::PROCESS_LAUNCHER);
+
+ std::unique_ptr<FileDescriptorInfo> files_to_register =
+ CreateDefaultPosixFilesToMap(*command_line(), child_process_id(),
+ mojo_client_handle());
+
+#if defined(V8_USE_EXTERNAL_STARTUP_DATA)
+ bool snapshot_loaded = false;
+ base::MemoryMappedFile::Region unused_region;
+ base::PlatformFile natives_pf =
+ gin::V8Initializer::GetOpenNativesFileForChildProcesses(&unused_region);
+ DCHECK_GE(natives_pf, 0);
+ files_to_register->Share(kV8NativesDataDescriptor, natives_pf);
+
+ base::MemoryMappedFile::Region snapshot_region;
+ base::PlatformFile snapshot_pf =
+ gin::V8Initializer::GetOpenSnapshotFileForChildProcesses(
+ &snapshot_region);
+ // Failure to load the V8 snapshot is not necessarily an error. V8 can start
+ // up (slower) without the snapshot.
+ if (snapshot_pf != -1) {
+ snapshot_loaded = true;
+ files_to_register->Share(kV8SnapshotDataDescriptor, snapshot_pf);
+ }
+ if (GetProcessType() != switches::kZygoteProcess) {
+ command_line()->AppendSwitch(::switches::kV8NativesPassedByFD);
+ if (snapshot_loaded) {
+ command_line()->AppendSwitch(::switches::kV8SnapshotPassedByFD);
+ }
+ }
+#endif // defined(V8_USE_EXTERNAL_STARTUP_DATA)
+
+ return files_to_register;
+}
+
+void ChildProcessLauncherHelper::BeforeLaunchOnLauncherThread(
+ const FileDescriptorInfo& files_to_register,
+ base::LaunchOptions* options) {
+ // Convert FD mapping to FileHandleMappingVector
+ std::unique_ptr<base::FileHandleMappingVector> fds_to_map =
+ files_to_register.GetMappingWithIDAdjustment(
+ base::GlobalDescriptors::kBaseDescriptor);
+
+ if (GetProcessType() == switches::kRendererProcess) {
+ const int sandbox_fd =
+ RenderSandboxHostLinux::GetInstance()->GetRendererSocket();
+ fds_to_map->push_back(std::make_pair(sandbox_fd, GetSandboxFD()));
+ }
+
+ options->environ = delegate_->GetEnvironment();
+ // fds_to_remap will de deleted in AfterLaunchOnLauncherThread() below.
+ options->fds_to_remap = fds_to_map.release();
+}
+
+ChildProcessLauncherHelper::Process
+ChildProcessLauncherHelper::LaunchProcessOnLauncherThread(
+ const base::LaunchOptions& options,
+ std::unique_ptr<FileMappedForLaunch> files_to_register,
+ bool* is_synchronous_launch,
+ int* launch_result) {
+ *is_synchronous_launch = true;
+
+ ZygoteHandle* zygote_handle =
+ base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kNoZygote) ?
+ nullptr : delegate_->GetZygote();
+ if (zygote_handle) {
+ // This code runs on the PROCESS_LAUNCHER thread so race conditions are not
+ // an issue with the lazy initialization.
+ if (*zygote_handle == nullptr) {
+ *zygote_handle = CreateZygote();
+ }
+ base::ProcessHandle handle = (*zygote_handle)->ForkRequest(
+ command_line()->argv(),
+ std::move(files_to_register),
+ GetProcessType());
+ *launch_result = LAUNCH_RESULT_SUCCESS;
+ Process process;
+ process.process = base::Process(handle);
+ process.zygote = *zygote_handle;
+ return process;
+ }
+
+ Process process;
+ process.process = base::LaunchProcess(*command_line(), options);
+ *launch_result = process.process.IsValid() ? LAUNCH_RESULT_SUCCESS
+ : LAUNCH_RESULT_FAILURE;
+ return process;
+}
+
+void ChildProcessLauncherHelper::AfterLaunchOnLauncherThread(
+ const ChildProcessLauncherHelper::Process& process,
+ const base::LaunchOptions& options) {
+ delete options.fds_to_remap;
+}
+
+// static
+base::TerminationStatus ChildProcessLauncherHelper::GetTerminationStatus(
+ const ChildProcessLauncherHelper::Process& process,
+ bool known_dead,
+ int* exit_code) {
+ if (process.zygote) {
+ return process.zygote->GetTerminationStatus(
+ process.process.Handle(), known_dead, exit_code);
+ }
+ if (known_dead) {
+ return base::GetKnownDeadTerminationStatus(
+ process.process.Handle(), exit_code);
+ }
+ return base::GetTerminationStatus(process.process.Handle(), exit_code);
+}
+
+// static
+bool ChildProcessLauncherHelper::TerminateProcess(
+ const base::Process& process, int exit_code, bool wait) {
+ return process.Terminate(exit_code, wait);
+}
+
+// static
+void ChildProcessLauncherHelper::ForceNormalProcessTerminationSync(
+ ChildProcessLauncherHelper::Process process) {
+ process.process.Terminate(RESULT_CODE_NORMAL_EXIT, false);
+ // On POSIX, we must additionally reap the child.
+ if (process.zygote) {
+ // If the renderer was created via a zygote, we have to proxy the reaping
+ // through the zygote process.
+ process.zygote->EnsureProcessTerminated(process.process.Handle());
+ } else {
+ base::EnsureProcessTerminated(std::move(process.process));
+ }
+}
+
+// static
+void ChildProcessLauncherHelper::SetProcessBackgroundedOnLauncherThread(
+ base::Process process, bool background) {
+ DCHECK_CURRENTLY_ON(BrowserThread::PROCESS_LAUNCHER);
+ if (process.CanBackgroundProcesses())
+ process.SetProcessBackgrounded(background);
+}
+
+} // namespace internal
+} // namespace content
diff --git a/content/browser/child_process_launcher_helper_mac.cc b/content/browser/child_process_launcher_helper_mac.cc
new file mode 100644
index 0000000..9a8bda0
--- /dev/null
+++ b/content/browser/child_process_launcher_helper_mac.cc
@@ -0,0 +1,150 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/memory/ptr_util.h"
+#include "base/posix/global_descriptors.h"
+#include "content/browser/bootstrap_sandbox_manager_mac.h"
+#include "content/browser/child_process_launcher.h"
+#include "content/browser/child_process_launcher_helper.h"
+#include "content/browser/child_process_launcher_helper_posix.h"
+#include "content/browser/mach_broker_mac.h"
+#include "content/public/common/result_codes.h"
+#include "content/public/common/sandboxed_process_launcher_delegate.h"
+#include "mojo/edk/embedder/scoped_platform_handle.h"
+#include "sandbox/mac/bootstrap_sandbox.h"
+#include "sandbox/mac/pre_exec_delegate.h"
+
+namespace content {
+namespace internal {
+
+mojo::edk::ScopedPlatformHandle
+ChildProcessLauncherHelper::PrepareMojoPipeHandlesOnClientThread() {
+ DCHECK_CURRENTLY_ON(client_thread_id_);
+ return mojo::edk::ScopedPlatformHandle();
+}
+
+void ChildProcessLauncherHelper::BeforeLaunchOnClientThread() {
+ DCHECK_CURRENTLY_ON(client_thread_id_);
+}
+
+std::unique_ptr<FileDescriptorInfo>
+ChildProcessLauncherHelper::GetFilesToMap() {
+ DCHECK_CURRENTLY_ON(BrowserThread::PROCESS_LAUNCHER);
+ return CreateDefaultPosixFilesToMap(*command_line(), child_process_id(),
+ mojo_client_handle());
+}
+
+void ChildProcessLauncherHelper::BeforeLaunchOnLauncherThread(
+ const FileMappedForLaunch& files_to_register,
+ base::LaunchOptions* options) {
+ // Convert FD mapping to FileHandleMappingVector.
+ std::unique_ptr<base::FileHandleMappingVector> fds_to_map =
+ files_to_register.GetMappingWithIDAdjustment(
+ base::GlobalDescriptors::kBaseDescriptor);
+
+ options->environ = delegate_->GetEnvironment();
+ // fds_to_remap will de deleted in AfterLaunchOnLauncherThread() below.
+ options->fds_to_remap = fds_to_map.release();
+
+ // Hold the MachBroker lock for the duration of LaunchProcess. The child will
+ // send its task port to the parent almost immediately after startup. The Mach
+ // message will be delivered to the parent, but updating the record of the
+ // launch will wait until after the placeholder PID is inserted below. This
+ // ensures that while the child process may send its port to the parent prior
+ // to the parent leaving LaunchProcess, the order in which the record in
+ // MachBroker is updated is correct.
+ MachBroker* broker = MachBroker::GetInstance();
+ broker->GetLock().Acquire();
+
+ // Make sure the MachBroker is running, and inform it to expect a check-in
+ // from the new process.
+ broker->EnsureRunning();
+
+ const SandboxType sandbox_type = delegate_->GetSandboxType();
+ std::unique_ptr<sandbox::PreExecDelegate> pre_exec_delegate;
+ if (BootstrapSandboxManager::ShouldEnable()) {
+ BootstrapSandboxManager* sandbox_manager =
+ BootstrapSandboxManager::GetInstance();
+ if (sandbox_manager->EnabledForSandbox(sandbox_type)) {
+ pre_exec_delegate = sandbox_manager->sandbox()->NewClient(sandbox_type);
+ }
+ }
+ // options now owns the pre_exec_delegate which will be delete on
+ // AfterLaunchOnLauncherThread below.
+ options->pre_exec_delegate = pre_exec_delegate.release();
+}
+
+ChildProcessLauncherHelper::Process
+ChildProcessLauncherHelper::LaunchProcessOnLauncherThread(
+ const base::LaunchOptions& options,
+ std::unique_ptr<FileDescriptorInfo> files_to_register,
+ bool* is_synchronous_launch,
+ int* launch_result) {
+ *is_synchronous_launch = true;
+ ChildProcessLauncherHelper::Process process;
+ process.process = base::LaunchProcess(*command_line(), options);
+ *launch_result = process.process.IsValid() ? LAUNCH_RESULT_SUCCESS
+ : LAUNCH_RESULT_FAILURE;
+ return process;
+}
+
+void ChildProcessLauncherHelper::AfterLaunchOnLauncherThread(
+ const ChildProcessLauncherHelper::Process& process,
+ const base::LaunchOptions& options) {
+ delete options.fds_to_remap;
+
+ std::unique_ptr<sandbox::PreExecDelegate> pre_exec_delegate =
+ base::WrapUnique(static_cast<sandbox::PreExecDelegate*>(
+ options.pre_exec_delegate));
+
+ MachBroker* broker = MachBroker::GetInstance();
+ if (process.process.IsValid()) {
+ broker->AddPlaceholderForPid(process.process.Pid(), child_process_id());
+ } else {
+ if (pre_exec_delegate) {
+ BootstrapSandboxManager::GetInstance()->sandbox()->RevokeToken(
+ pre_exec_delegate->sandbox_token());
+ }
+ }
+
+ // After updating the broker, release the lock and let the child's message be
+ // processed on the broker's thread.
+ broker->GetLock().Release();
+}
+
+// static
+base::TerminationStatus ChildProcessLauncherHelper::GetTerminationStatus(
+ const ChildProcessLauncherHelper::Process& process,
+ bool known_dead,
+ int* exit_code) {
+ return known_dead
+ ? base::GetKnownDeadTerminationStatus(process.process.Handle(), exit_code)
+ : base::GetTerminationStatus(process.process.Handle(), exit_code);
+}
+
+// static
+bool ChildProcessLauncherHelper::TerminateProcess(
+ const base::Process& process, int exit_code, bool wait) {
+ return process.Terminate(exit_code, wait);
+}
+
+// static
+void ChildProcessLauncherHelper::ForceNormalProcessTerminationSync(
+ ChildProcessLauncherHelper::Process process) {
+ DCHECK_CURRENTLY_ON(BrowserThread::PROCESS_LAUNCHER);
+ // Client has gone away, so just kill the process. Using exit code 0 means
+ // that UMA won't treat this as a crash.
+ process.process.Terminate(RESULT_CODE_NORMAL_EXIT, false);
+ base::EnsureProcessTerminated(std::move(process.process));
+}
+
+// static
+void ChildProcessLauncherHelper::SetProcessBackgroundedOnLauncherThread(
+ base::Process process, bool background) {
+ if (process.CanBackgroundProcesses())
+ process.SetProcessBackgrounded(MachBroker::GetInstance(), background);
+}
+
+} // namespace internal
+} // namespace content
diff --git a/content/browser/child_process_launcher_helper_posix.cc b/content/browser/child_process_launcher_helper_posix.cc
new file mode 100644
index 0000000..9c3583e
--- /dev/null
+++ b/content/browser/child_process_launcher_helper_posix.cc
@@ -0,0 +1,43 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/child_process_launcher_helper_posix.h"
+
+#include "base/command_line.h"
+#include "base/metrics/field_trial.h"
+#include "content/browser/file_descriptor_info_impl.h"
+#include "content/public/browser/content_browser_client.h"
+#include "content/public/common/content_client.h"
+#include "content/public/common/content_descriptors.h"
+#include "mojo/edk/embedder/platform_handle.h"
+
+namespace content {
+namespace internal {
+
+std::unique_ptr<FileDescriptorInfo> CreateDefaultPosixFilesToMap(
+ const base::CommandLine& command_line,
+ int child_process_id,
+ const mojo::edk::PlatformHandle& mojo_client_handle) {
+ std::unique_ptr<FileDescriptorInfo> files_to_register(
+ FileDescriptorInfoImpl::Create());
+
+ int field_trial_handle = base::FieldTrialList::GetFieldTrialHandle();
+ if (field_trial_handle != base::kInvalidPlatformFile)
+ files_to_register->Share(kFieldTrialDescriptor, field_trial_handle);
+
+ DCHECK(mojo_client_handle.is_valid());
+ files_to_register->Share(kMojoIPCChannel, mojo_client_handle.handle);
+
+ // TODO(jcivelli): remove this "if defined" by making
+ // GetAdditionalMappedFilesForChildProcess a no op on Mac.
+#if !defined(OS_MACOSX)
+ GetContentClient()->browser()->GetAdditionalMappedFilesForChildProcess(
+ command_line, child_process_id, files_to_register.get());
+#endif
+
+ return files_to_register;
+}
+
+} // namespace internal
+} // namespace content
diff --git a/content/browser/child_process_launcher_helper_posix.h b/content/browser/child_process_launcher_helper_posix.h
new file mode 100644
index 0000000..3b7a2aeb
--- /dev/null
+++ b/content/browser/child_process_launcher_helper_posix.h
@@ -0,0 +1,38 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_CHILD_PROCESS_LAUNCHER_HELPER_POSIX_H_
+#define CONTENT_BROWSER_CHILD_PROCESS_LAUNCHER_HELPER_POSIX_H_
+
+#include <memory>
+
+namespace base {
+class CommandLine;
+} // namespace base
+
+namespace mojo {
+namespace edk {
+struct PlatformHandle;
+} // namespace mojo
+} // namespace edk
+
+// Contains the common functionalities between the various POSIX child process
+// launcher implementations.
+
+namespace content {
+
+class FileDescriptorInfo;
+
+namespace internal {
+
+std::unique_ptr<FileDescriptorInfo> CreateDefaultPosixFilesToMap(
+ const base::CommandLine& command_line,
+ int child_process_id,
+ const mojo::edk::PlatformHandle& mojo_client_handle);
+
+} // namespace internal
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_CHILD_PROCESS_LAUNCHER_HELPER_POSIX_H_
\ No newline at end of file
diff --git a/content/browser/child_process_launcher_helper_win.cc b/content/browser/child_process_launcher_helper_win.cc
new file mode 100644
index 0000000..90d4bdc8
--- /dev/null
+++ b/content/browser/child_process_launcher_helper_win.cc
@@ -0,0 +1,119 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/files/file_path.h"
+#include "base/metrics/field_trial.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/win/scoped_handle.h"
+#include "base/win/win_util.h"
+#include "content/browser/child_process_launcher_helper.h"
+#include "content/common/sandbox_win.h"
+#include "content/public/common/result_codes.h"
+#include "content/public/common/sandbox_init.h"
+#include "content/public/common/sandboxed_process_launcher_delegate.h"
+#include "mojo/edk/embedder/named_platform_channel_pair.h"
+#include "mojo/edk/embedder/platform_channel_pair.h"
+#include "mojo/edk/embedder/scoped_platform_handle.h"
+#include "sandbox/win/src/sandbox_types.h"
+
+namespace content {
+namespace internal {
+
+void ChildProcessLauncherHelper::BeforeLaunchOnClientThread() {
+ DCHECK_CURRENTLY_ON(client_thread_id_);
+}
+
+mojo::edk::ScopedPlatformHandle
+ChildProcessLauncherHelper::PrepareMojoPipeHandlesOnClientThread() {
+ DCHECK_CURRENTLY_ON(client_thread_id_);
+
+ if (!delegate_->ShouldLaunchElevated())
+ return mojo::edk::ScopedPlatformHandle();
+
+ mojo::edk::NamedPlatformChannelPair named_pair;
+ named_pair.PrepareToPassClientHandleToChildProcess(command_line());
+ return named_pair.PassServerHandle();
+}
+
+std::unique_ptr<FileMappedForLaunch>
+ChildProcessLauncherHelper::GetFilesToMap() {
+ return std::unique_ptr<FileMappedForLaunch>();
+}
+
+void ChildProcessLauncherHelper::BeforeLaunchOnLauncherThread(
+ const FileMappedForLaunch& files_to_register,
+ base::LaunchOptions* options) {
+ DCHECK_CURRENTLY_ON(BrowserThread::PROCESS_LAUNCHER);
+}
+
+ChildProcessLauncherHelper::Process
+ChildProcessLauncherHelper::LaunchProcessOnLauncherThread(
+ const base::LaunchOptions& options,
+ std::unique_ptr<FileMappedForLaunch> files_to_register,
+ bool* is_synchronous_launch,
+ int* launch_result) {
+ DCHECK_CURRENTLY_ON(BrowserThread::PROCESS_LAUNCHER);
+ *is_synchronous_launch = true;
+ if (delegate_->ShouldLaunchElevated()) {
+ // When establishing a Mojo connection, the pipe path has already been added
+ // to the command line.
+ base::LaunchOptions win_options;
+ win_options.start_hidden = true;
+ ChildProcessLauncherHelper::Process process;
+ process.process = base::LaunchElevatedProcess(*command_line(), win_options);
+ return process;
+ }
+ base::HandlesToInheritVector handles;
+ handles.push_back(mojo_client_handle().handle);
+ base::FieldTrialList::AppendFieldTrialHandleIfNeeded(&handles);
+ command_line()->AppendSwitchASCII(
+ mojo::edk::PlatformChannelPair::kMojoPlatformChannelHandleSwitch,
+ base::UintToString(base::win::HandleToUint32(handles[0])));
+ ChildProcessLauncherHelper::Process process;
+ *launch_result = StartSandboxedProcess(
+ delegate_.get(),
+ command_line(),
+ handles,
+ &process.process);
+ return process;
+}
+
+void ChildProcessLauncherHelper::AfterLaunchOnLauncherThread(
+ const ChildProcessLauncherHelper::Process& process,
+ const base::LaunchOptions& options) {
+ DCHECK_CURRENTLY_ON(BrowserThread::PROCESS_LAUNCHER);
+}
+
+// static
+base::TerminationStatus ChildProcessLauncherHelper::GetTerminationStatus(
+ const ChildProcessLauncherHelper::Process& process,
+ bool known_dead,
+ int* exit_code) {
+ return base::GetTerminationStatus(process.process.Handle(), exit_code);
+}
+
+// static
+bool ChildProcessLauncherHelper::TerminateProcess(
+ const base::Process& process, int exit_code, bool wait) {
+ return process.Terminate(exit_code, wait);
+}
+
+void ChildProcessLauncherHelper::ForceNormalProcessTerminationSync(
+ ChildProcessLauncherHelper::Process process) {
+ DCHECK_CURRENTLY_ON(BrowserThread::PROCESS_LAUNCHER);
+ // Client has gone away, so just kill the process. Using exit code 0 means
+ // that UMA won't treat this as a crash.
+ process.process.Terminate(RESULT_CODE_NORMAL_EXIT, false);
+}
+
+// static
+void ChildProcessLauncherHelper::SetProcessBackgroundedOnLauncherThread(
+ base::Process process, bool background) {
+ DCHECK_CURRENTLY_ON(BrowserThread::PROCESS_LAUNCHER);
+ if (process.CanBackgroundProcesses())
+ process.SetProcessBackgrounded(background);
+}
+
+} // namespace internal
+} // namespace content
diff --git a/content/browser/file_descriptor_info_impl.cc b/content/browser/file_descriptor_info_impl.cc
index 3392819..1343e7c 100644
--- a/content/browser/file_descriptor_info_impl.cc
+++ b/content/browser/file_descriptor_info_impl.cc
@@ -6,6 +6,7 @@
#include <utility>
+#include "base/memory/ptr_util.h"
#include "base/stl_util.h"
namespace content {
@@ -93,12 +94,13 @@
return mapping_;
}
-base::FileHandleMappingVector
+std::unique_ptr<base::FileHandleMappingVector>
FileDescriptorInfoImpl::GetMappingWithIDAdjustment(int delta) const {
- base::FileHandleMappingVector result = mapping_;
+ std::unique_ptr<base::FileHandleMappingVector> result =
+ base::MakeUnique<base::FileHandleMappingVector>(mapping_);
// Adding delta to each ID.
for (unsigned i = 0; i < mapping_.size(); ++i)
- result[i].second += delta;
+ (*result)[i].second += delta;
return result;
}
diff --git a/content/browser/file_descriptor_info_impl.h b/content/browser/file_descriptor_info_impl.h
index eba0937..a1a7085 100644
--- a/content/browser/file_descriptor_info_impl.h
+++ b/content/browser/file_descriptor_info_impl.h
@@ -27,7 +27,7 @@
const base::MemoryMappedFile::Region& region) override;
void Transfer(int id, base::ScopedFD fd) override;
const base::FileHandleMappingVector& GetMapping() const override;
- base::FileHandleMappingVector GetMappingWithIDAdjustment(
+ std::unique_ptr<base::FileHandleMappingVector> GetMappingWithIDAdjustment(
int delta) const override;
base::PlatformFile GetFDAt(size_t i) const override;
int GetIDAt(size_t i) const override;
diff --git a/content/browser/file_descriptor_info_impl_unittest.cc b/content/browser/file_descriptor_info_impl_unittest.cc
index e14dc312..1d442f79 100644
--- a/content/browser/file_descriptor_info_impl_unittest.cc
+++ b/content/browser/file_descriptor_info_impl_unittest.cc
@@ -77,10 +77,10 @@
target->Transfer(testingId1, base::ScopedFD(GetSafeFd()));
target->Transfer(testingId2, base::ScopedFD(GetSafeFd()));
- base::FileHandleMappingVector mapping =
+ std::unique_ptr<base::FileHandleMappingVector> mapping =
target->GetMappingWithIDAdjustment(100);
- ASSERT_EQ(mapping[0].second, 142);
- ASSERT_EQ(mapping[1].second, 143);
+ ASSERT_EQ((*mapping)[0].second, 142);
+ ASSERT_EQ((*mapping)[1].second, 143);
}
} // namespace content
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc
index de8b9ed..bd1dbf4 100644
--- a/content/browser/renderer_host/render_process_host_impl.cc
+++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -198,7 +198,6 @@
#include "ui/native_theme/native_theme_switches.h"
#if defined(OS_ANDROID)
-#include "content/browser/android/child_process_launcher_android.h"
#include "content/browser/screen_orientation/screen_orientation_listener_android.h"
#include "content/public/browser/android/java_interfaces.h"
#include "ipc/ipc_sync_channel.h"
@@ -1940,16 +1939,10 @@
if (run_renderer_in_process())
return false; // Single process mode never shuts down the renderer.
-#if defined(OS_ANDROID)
- // Android requires a different approach for killing.
- StopChildProcess(GetHandle());
- return true;
-#else
- if (!child_process_launcher_.get() || child_process_launcher_->IsStarting())
+ if (!child_process_launcher_.get())
return false;
- return child_process_launcher_->GetProcess().Terminate(exit_code, wait);
-#endif
+ return child_process_launcher_->Terminate(exit_code, wait);
}
bool RenderProcessHostImpl::FastShutdownIfPossible() {
diff --git a/content/public/browser/browser_message_filter.cc b/content/public/browser/browser_message_filter.cc
index 4fcbe31..a6521d3 100644
--- a/content/public/browser/browser_message_filter.cc
+++ b/content/public/browser/browser_message_filter.cc
@@ -14,16 +14,13 @@
#include "base/task_runner.h"
#include "build/build_config.h"
#include "content/browser/browser_child_process_host_impl.h"
+#include "content/browser/child_process_launcher.h"
#include "content/public/browser/user_metrics.h"
#include "content/public/common/content_switches.h"
#include "content/public/common/result_codes.h"
#include "ipc/ipc_sync_message.h"
#include "ipc/message_filter.h"
-#if defined(OS_ANDROID)
-#include "content/browser/android/child_process_launcher_android.h"
-#endif
-
using content::BrowserMessageFilter;
namespace content {
@@ -164,12 +161,8 @@
if (command_line->HasSwitch(switches::kDisableKillAfterBadIPC))
return;
-#if defined(OS_ANDROID)
- // Android requires a different approach for killing.
- StopChildProcess(peer_process_.Handle());
-#else
- peer_process_.Terminate(content::RESULT_CODE_KILLED_BAD_MESSAGE, false);
-#endif
+ ChildProcessLauncher::TerminateProcess(
+ peer_process_, content::RESULT_CODE_KILLED_BAD_MESSAGE, false);
// Report a crash, since none will be generated by the killed renderer.
base::debug::DumpWithoutCrashing();
diff --git a/content/public/browser/file_descriptor_info.h b/content/public/browser/file_descriptor_info.h
index 4fc686b..ef44a08 100644
--- a/content/public/browser/file_descriptor_info.h
+++ b/content/public/browser/file_descriptor_info.h
@@ -7,6 +7,8 @@
#include <stddef.h>
+#include <memory>
+
#include "base/files/file.h"
#include "base/files/memory_mapped_file.h"
#include "base/process/launch.h"
@@ -41,8 +43,8 @@
// A GetMapping() variant that adjusts the ID value by |delta|.
// Some environments need this trick.
- virtual base::FileHandleMappingVector GetMappingWithIDAdjustment(
- int delta) const = 0;
+ virtual std::unique_ptr<base::FileHandleMappingVector>
+ GetMappingWithIDAdjustment(int delta) const = 0;
// API for iterating over the registered ID-FD pairs.
virtual base::PlatformFile GetFDAt(size_t i) const = 0;