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/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