Initial implementation for sharing field trial state (win)

This plumbs the field trial state string from the gpu and renderer host
processes and puts it in shared memory, passes the shared memory handle
(and length) via a command-line flag to the child, and the child will
initialize it there.

BUG=131632

Review-Url: https://codereview.chromium.org/2365273004
Cr-Commit-Position: refs/heads/master@{#425013}
diff --git a/base/metrics/field_trial.cc b/base/metrics/field_trial.cc
index 45ee22d..4f3e0c7 100644
--- a/base/metrics/field_trial.cc
+++ b/base/metrics/field_trial.cc
@@ -7,7 +7,10 @@
 #include <algorithm>
 #include <utility>
 
+#include "base/base_switches.h"
 #include "base/build_time.h"
+#include "base/command_line.h"
+#include "base/feature_list.h"
 #include "base/logging.h"
 #include "base/rand_util.h"
 #include "base/strings/string_number_conversions.h"
@@ -28,6 +31,14 @@
 // command line which forces its activation.
 const char kActivationMarker = '*';
 
+// Use shared memory to communicate field trial (experiment) state. Set to false
+// for now while the implementation is fleshed out (e.g. data format, single
+// shared memory segment). See https://codereview.chromium.org/2365273004/ and
+// crbug.com/653874
+#if defined(OS_WIN)
+const bool kUseSharedMemoryForFieldTrials = false;
+#endif
+
 // Created a time value based on |year|, |month| and |day_of_month| parameters.
 Time CreateTimeFromParams(int year, int month, int day_of_month) {
   DCHECK_GT(year, 1970);
@@ -558,6 +569,77 @@
 }
 
 // static
+void FieldTrialList::CreateTrialsFromCommandLine(
+    const base::CommandLine& cmd_line,
+    const char* field_trial_handle_switch) {
+  DCHECK(global_);
+
+#if defined(OS_WIN)
+  if (cmd_line.HasSwitch(field_trial_handle_switch)) {
+    std::string arg = cmd_line.GetSwitchValueASCII(field_trial_handle_switch);
+    size_t token = arg.find(",");
+    int field_trial_handle = std::stoi(arg.substr(0, token));
+    int field_trial_length = std::stoi(arg.substr(token + 1, arg.length()));
+
+    HANDLE handle = reinterpret_cast<HANDLE>(field_trial_handle);
+    base::SharedMemoryHandle shm_handle =
+        base::SharedMemoryHandle(handle, base::GetCurrentProcId());
+
+    // Gets deleted when it gets out of scope, but that's OK because we need it
+    // only for the duration of this call currently anyway.
+    base::SharedMemory shared_memory(shm_handle, false);
+    shared_memory.Map(field_trial_length);
+
+    char* field_trial_state = static_cast<char*>(shared_memory.memory());
+    bool result = FieldTrialList::CreateTrialsFromString(
+        std::string(field_trial_state), std::set<std::string>());
+    DCHECK(result);
+    return;
+  }
+#endif
+
+  if (cmd_line.HasSwitch(switches::kForceFieldTrials)) {
+    bool result = FieldTrialList::CreateTrialsFromString(
+        cmd_line.GetSwitchValueASCII(switches::kForceFieldTrials),
+        std::set<std::string>());
+    DCHECK(result);
+  }
+}
+
+// static
+std::unique_ptr<base::SharedMemory> FieldTrialList::CopyFieldTrialStateToFlags(
+    const char* field_trial_handle_switch,
+    base::CommandLine* cmd_line) {
+  std::string field_trial_states;
+  base::FieldTrialList::AllStatesToString(&field_trial_states);
+  if (!field_trial_states.empty()) {
+// Use shared memory to pass the state if the feature is enabled, otherwise
+// fallback to passing it via the command line as a string.
+#if defined(OS_WIN)
+    if (kUseSharedMemoryForFieldTrials) {
+      std::unique_ptr<base::SharedMemory> shm(new base::SharedMemory());
+      size_t length = field_trial_states.size() + 1;
+      shm->CreateAndMapAnonymous(length);
+      memcpy(shm->memory(), field_trial_states.c_str(), length);
+
+      // HANDLE is just typedef'd to void *
+      auto uintptr_handle =
+          reinterpret_cast<std::uintptr_t>(shm->handle().GetHandle());
+      std::string field_trial_handle =
+          std::to_string(uintptr_handle) + "," + std::to_string(length);
+
+      cmd_line->AppendSwitchASCII(field_trial_handle_switch,
+                                  field_trial_handle);
+      return shm;
+    }
+#endif
+    cmd_line->AppendSwitchASCII(switches::kForceFieldTrials,
+                                field_trial_states);
+  }
+  return std::unique_ptr<base::SharedMemory>(nullptr);
+}
+
+// static
 FieldTrial* FieldTrialList::CreateFieldTrial(
     const std::string& name,
     const std::string& group_name) {
diff --git a/base/metrics/field_trial.h b/base/metrics/field_trial.h
index 2b88949..99e705a 100644
--- a/base/metrics/field_trial.h
+++ b/base/metrics/field_trial.h
@@ -64,9 +64,11 @@
 #include <vector>
 
 #include "base/base_export.h"
+#include "base/command_line.h"
 #include "base/gtest_prod_util.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
+#include "base/memory/shared_memory.h"
 #include "base/observer_list_threadsafe.h"
 #include "base/strings/string_piece.h"
 #include "base/synchronization/lock.h"
@@ -464,6 +466,28 @@
       const std::string& trials_string,
       const std::set<std::string>& ignored_trial_names);
 
+  // Achieves the same thing as CreateTrialsFromString, except wraps the logic
+  // by taking in the trials from the command line, either via shared memory
+  // handle or command line argument.
+  // If using shared memory to pass around the list of field trials, then
+  // expects |field_trial_handle_switch| command line argument to
+  // contain the shared memory handle.
+  // If not, then create the trials as before (using the kForceFieldTrials
+  // switch). Needs the |field_trial_handle_switch| argument to be passed in
+  // since base/ can't depend on content/.
+  static void CreateTrialsFromCommandLine(
+      const base::CommandLine& cmd_line,
+      const char* field_trial_handle_switch);
+
+  // Adds a switch to the command line containing the field trial state as a
+  // string (if not using shared memory to share field trial state), or the
+  // shared memory handle + length.
+  // Needs the |field_trial_handle_switch| argument to be passed in since base/
+  // can't depend on content/.
+  static std::unique_ptr<base::SharedMemory> CopyFieldTrialStateToFlags(
+      const char* field_trial_handle_switch,
+      base::CommandLine* cmd_line);
+
   // Create a FieldTrial with the given |name| and using 100% probability for
   // the FieldTrial, force FieldTrial to have the same group string as
   // |group_name|. This is commonly used in a non-browser process, to carry
diff --git a/base/metrics/field_trial_unittest.cc b/base/metrics/field_trial_unittest.cc
index caa00e8..bb45bd71 100644
--- a/base/metrics/field_trial_unittest.cc
+++ b/base/metrics/field_trial_unittest.cc
@@ -6,14 +6,18 @@
 
 #include <stddef.h>
 
+#include "base/base_switches.h"
 #include "base/build_time.h"
+#include "base/feature_list.h"
 #include "base/macros.h"
+#include "base/memory/ptr_util.h"
 #include "base/message_loop/message_loop.h"
 #include "base/rand_util.h"
 #include "base/run_loop.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/stringprintf.h"
 #include "base/test/gtest_util.h"
+#include "base/test/mock_entropy_provider.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace base {
@@ -1132,4 +1136,19 @@
       "");
 }
 
+TEST(FieldTrialListTest, TestCopyFieldTrialStateToFlags) {
+  base::FieldTrialList field_trial_list(
+      base::MakeUnique<base::MockEntropyProvider>());
+  base::FieldTrialList::CreateFieldTrial("Trial1", "Group1");
+  base::FilePath test_file_path = base::FilePath(FILE_PATH_LITERAL("Program"));
+  base::CommandLine cmd_line = base::CommandLine(test_file_path);
+
+  std::unique_ptr<base::SharedMemory> field_trial_state =
+      base::FieldTrialList::CopyFieldTrialStateToFlags("field-trial-handle",
+                                                       &cmd_line);
+
+  EXPECT_TRUE(field_trial_state.get() == nullptr);
+  EXPECT_TRUE(cmd_line.HasSwitch(switches::kForceFieldTrials));
+}
+
 }  // namespace base
diff --git a/components/nacl/browser/nacl_broker_host_win.cc b/components/nacl/browser/nacl_broker_host_win.cc
index 21bd188a..501ba6c6 100644
--- a/components/nacl/browser/nacl_broker_host_win.cc
+++ b/components/nacl/browser/nacl_broker_host_win.cc
@@ -73,9 +73,8 @@
   if (NaClBrowser::GetDelegate()->DialogsAreSuppressed())
     cmd_line->AppendSwitch(switches::kNoErrorDialogs);
 
-  process_->Launch(new NaClBrokerSandboxedProcessLauncherDelegate,
-                   cmd_line,
-                   true);
+  process_->Launch(new NaClBrokerSandboxedProcessLauncherDelegate, cmd_line,
+                   nullptr, true);
   return true;
 }
 
diff --git a/components/nacl/browser/nacl_process_host.cc b/components/nacl/browser/nacl_process_host.cc
index 7f23abe..ecd66b01 100644
--- a/components/nacl/browser/nacl_process_host.cc
+++ b/components/nacl/browser/nacl_process_host.cc
@@ -668,8 +668,7 @@
 #endif
   process_->Launch(
       new NaClSandboxedProcessLauncherDelegate(process_->GetHost()),
-      cmd_line.release(),
-      true);
+      cmd_line.release(), nullptr, true);
   return true;
 }
 
diff --git a/content/app/content_main_runner.cc b/content/app/content_main_runner.cc
index a8e8660..d46acd2 100644
--- a/content/app/content_main_runner.cc
+++ b/content/app/content_main_runner.cc
@@ -32,6 +32,7 @@
 #include "base/path_service.h"
 #include "base/process/launch.h"
 #include "base/process/memory.h"
+#include "base/process/process.h"
 #include "base/process/process_handle.h"
 #include "base/profiler/scoped_tracker.h"
 #include "base/strings/string_number_conversions.h"
@@ -142,12 +143,8 @@
 
   // Ensure any field trials in browser are reflected into the child
   // process.
-  if (command_line.HasSwitch(switches::kForceFieldTrials)) {
-    bool result = base::FieldTrialList::CreateTrialsFromString(
-        command_line.GetSwitchValueASCII(switches::kForceFieldTrials),
-        std::set<std::string>());
-    DCHECK(result);
-  }
+  base::FieldTrialList::CreateTrialsFromCommandLine(
+      command_line, switches::kFieldTrialHandle);
 
   std::unique_ptr<base::FeatureList> feature_list(new base::FeatureList);
   feature_list->InitializeFromCommandLine(
diff --git a/content/browser/browser_child_process_host_impl.cc b/content/browser/browser_child_process_host_impl.cc
index a8d19b0..bb6a646 100644
--- a/content/browser/browser_child_process_host_impl.cc
+++ b/content/browser/browser_child_process_host_impl.cc
@@ -38,6 +38,7 @@
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/child_process_data.h"
 #include "content/public/browser/content_browser_client.h"
+#include "content/public/common/content_features.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/common/mojo_channel_switches.h"
 #include "content/public/common/process_type.h"
@@ -207,7 +208,8 @@
 }
 
 // static
-void BrowserChildProcessHostImpl::CopyFeatureAndFieldTrialFlags(
+std::unique_ptr<base::SharedMemory>
+BrowserChildProcessHostImpl::CopyFeatureAndFieldTrialFlags(
     base::CommandLine* cmd_line) {
   std::string enabled_features;
   std::string disabled_features;
@@ -220,17 +222,14 @@
 
   // If we run base::FieldTrials, we want to pass to their state to the
   // child process so that it can act in accordance with each state.
-  std::string field_trial_states;
-  base::FieldTrialList::AllStatesToString(&field_trial_states);
-  if (!field_trial_states.empty()) {
-    cmd_line->AppendSwitchASCII(switches::kForceFieldTrials,
-                                field_trial_states);
-  }
+  return base::FieldTrialList::CopyFieldTrialStateToFlags(
+      switches::kFieldTrialHandle, cmd_line);
 }
 
 void BrowserChildProcessHostImpl::Launch(
     SandboxedProcessLauncherDelegate* delegate,
     base::CommandLine* cmd_line,
+    const base::SharedMemory* field_trial_state,
     bool terminate_on_shutdown) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
@@ -258,11 +257,7 @@
 
   notify_child_disconnected_ = true;
   child_process_.reset(new ChildProcessLauncher(
-      delegate,
-      cmd_line,
-      data_.id,
-      this,
-      child_token_,
+      delegate, cmd_line, data_.id, this, field_trial_state, child_token_,
       base::Bind(&BrowserChildProcessHostImpl::OnMojoError,
                  weak_factory_.GetWeakPtr(),
                  base::ThreadTaskRunnerHandle::Get()),
diff --git a/content/browser/browser_child_process_host_impl.h b/content/browser/browser_child_process_host_impl.h
index c10b9adf..c88ec6e 100644
--- a/content/browser/browser_child_process_host_impl.h
+++ b/content/browser/browser_child_process_host_impl.h
@@ -12,6 +12,7 @@
 
 #include "base/compiler_specific.h"
 #include "base/memory/ref_counted.h"
+#include "base/memory/shared_memory.h"
 #include "base/memory/weak_ptr.h"
 #include "base/process/process.h"
 #include "base/single_thread_task_runner.h"
@@ -65,12 +66,14 @@
   // Copies kEnableFeatures and kDisableFeatures to the command line. Generates
   // them from the FeatureList override state, to take into account overrides
   // from FieldTrials.
-  static void CopyFeatureAndFieldTrialFlags(base::CommandLine* cmd_line);
+  static std::unique_ptr<base::SharedMemory> CopyFeatureAndFieldTrialFlags(
+      base::CommandLine* cmd_line);
 
   // BrowserChildProcessHost implementation:
   bool Send(IPC::Message* message) override;
   void Launch(SandboxedProcessLauncherDelegate* delegate,
               base::CommandLine* cmd_line,
+              const base::SharedMemory* field_trial_state,
               bool terminate_on_shutdown) override;
   const ChildProcessData& GetData() const override;
   ChildProcessHost* GetHost() const override;
diff --git a/content/browser/child_process_launcher.cc b/content/browser/child_process_launcher.cc
index 0d640fa..82d6744b 100644
--- a/content/browser/child_process_launcher.cc
+++ b/content/browser/child_process_launcher.cc
@@ -121,6 +121,7 @@
 #if defined(OS_ANDROID)
                             base::ScopedFD ipcfd,
 #endif
+                            const base::SharedMemory* field_trial_state,
                             mojo::edk::ScopedPlatformHandle client_handle,
                             base::CommandLine* cmd_line) {
   DCHECK_CURRENTLY_ON(BrowserThread::PROCESS_LAUNCHER);
@@ -152,6 +153,8 @@
   } else {
     base::HandlesToInheritVector handles;
     handles.push_back(client_handle.get().handle);
+    if (field_trial_state)
+      handles.push_back(field_trial_state->handle().GetHandle());
     cmd_line->AppendSwitchASCII(
         mojo::edk::PlatformChannelPair::kMojoPlatformChannelHandleSwitch,
         base::UintToString(base::win::HandleToUint32(handles[0])));
@@ -397,6 +400,7 @@
     base::CommandLine* cmd_line,
     int child_process_id,
     Client* client,
+    const base::SharedMemory* field_trial_state,
     const std::string& mojo_child_token,
     const mojo::edk::ProcessErrorCallback& process_error_callback,
     bool terminate_on_shutdown)
@@ -417,7 +421,7 @@
       weak_factory_(this) {
   DCHECK(CalledOnValidThread());
   CHECK(BrowserThread::GetCurrentThreadIdentifier(&client_thread_id_));
-  Launch(delegate, cmd_line, child_process_id);
+  Launch(delegate, cmd_line, child_process_id, field_trial_state);
 }
 
 ChildProcessLauncher::~ChildProcessLauncher() {
@@ -431,10 +435,10 @@
   }
 }
 
-void ChildProcessLauncher::Launch(
-    SandboxedProcessLauncherDelegate* delegate,
-    base::CommandLine* cmd_line,
-    int child_process_id) {
+void ChildProcessLauncher::Launch(SandboxedProcessLauncherDelegate* delegate,
+                                  base::CommandLine* cmd_line,
+                                  int child_process_id,
+                                  const base::SharedMemory* field_trial_state) {
   DCHECK(CalledOnValidThread());
 
 #if defined(OS_ANDROID)
@@ -483,7 +487,7 @@
 #if defined(OS_ANDROID)
                  base::Passed(&ipcfd),
 #endif
-                 base::Passed(&client_handle), cmd_line));
+                 field_trial_state, base::Passed(&client_handle), cmd_line));
 }
 
 void ChildProcessLauncher::UpdateTerminationStatus(bool known_dead) {
diff --git a/content/browser/child_process_launcher.h b/content/browser/child_process_launcher.h
index b0cf02c..943aba2e 100644
--- a/content/browser/child_process_launcher.h
+++ b/content/browser/child_process_launcher.h
@@ -7,6 +7,7 @@
 
 #include "base/files/scoped_file.h"
 #include "base/macros.h"
+#include "base/memory/shared_memory.h"
 #include "base/memory/weak_ptr.h"
 #include "base/process/kill.h"
 #include "base/process/launch.h"
@@ -79,6 +80,7 @@
       base::CommandLine* cmd_line,
       int child_process_id,
       Client* client,
+      const base::SharedMemory* field_trial_state,
       const std::string& mojo_child_token,
       const mojo::edk::ProcessErrorCallback& process_error_callback,
       bool terminate_on_shutdown = true);
@@ -116,7 +118,8 @@
   // Posts a task to the launcher thread to do the actual work.
   void Launch(SandboxedProcessLauncherDelegate* delegate,
               base::CommandLine* cmd_line,
-              int child_process_id);
+              int child_process_id,
+              const base::SharedMemory* field_trial_state);
 
   void UpdateTerminationStatus(bool known_dead);
 
diff --git a/content/browser/gpu/gpu_process_host.cc b/content/browser/gpu/gpu_process_host.cc
index 42307944..d444801 100644
--- a/content/browser/gpu/gpu_process_host.cc
+++ b/content/browser/gpu/gpu_process_host.cc
@@ -970,8 +970,11 @@
 
   base::CommandLine* cmd_line = new base::CommandLine(exe_path);
 #endif
+
   cmd_line->AppendSwitchASCII(switches::kProcessType, switches::kGpuProcess);
-  BrowserChildProcessHostImpl::CopyFeatureAndFieldTrialFlags(cmd_line);
+
+  field_trial_state_ =
+      BrowserChildProcessHostImpl::CopyFeatureAndFieldTrialFlags(cmd_line);
 
 #if defined(OS_WIN)
   cmd_line->AppendArg(switches::kPrefetchArgumentGpu);
@@ -1015,10 +1018,8 @@
     cmd_line->PrependWrapper(gpu_launcher);
 
   process_->Launch(
-      new GpuSandboxedProcessLauncherDelegate(cmd_line,
-                                              process_->GetHost()),
-      cmd_line,
-      true);
+      new GpuSandboxedProcessLauncherDelegate(cmd_line, process_->GetHost()),
+      cmd_line, field_trial_state_.get(), true);
   process_launched_ = true;
 
   UMA_HISTOGRAM_ENUMERATION("GPU.GPUProcessLifetimeEvents",
diff --git a/content/browser/gpu/gpu_process_host.h b/content/browser/gpu/gpu_process_host.h
index d42c354..c8e66825 100644
--- a/content/browser/gpu/gpu_process_host.h
+++ b/content/browser/gpu/gpu_process_host.h
@@ -292,6 +292,12 @@
 
   std::string shader_prefix_key_;
 
+  // Anonymous shared memory segment to share with subprocess containing list of
+  // field trials (represented as a string).
+  // TODO(crbug.com/653874): Eventually remove this and use single shared memory
+  // object across processes.
+  std::unique_ptr<base::SharedMemory> field_trial_state_;
+
   DISALLOW_COPY_AND_ASSIGN(GpuProcessHost);
 };
 
diff --git a/content/browser/ppapi_plugin_process_host.cc b/content/browser/ppapi_plugin_process_host.cc
index 1bdf4a96..b5e3335 100644
--- a/content/browser/ppapi_plugin_process_host.cc
+++ b/content/browser/ppapi_plugin_process_host.cc
@@ -444,11 +444,9 @@
   // On posix, never use the zygote for the broker. Also, only use the zygote if
   // we are not using a plugin launcher - having a plugin launcher means we need
   // to use another process instead of just forking the zygote.
-  process_->Launch(
-      new PpapiPluginSandboxedProcessLauncherDelegate(is_broker_,
-                                                      process_->GetHost()),
-      cmd_line,
-      true);
+  process_->Launch(new PpapiPluginSandboxedProcessLauncherDelegate(
+                       is_broker_, process_->GetHost()),
+                   cmd_line, nullptr, true);
   return true;
 }
 
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc
index 3c9fe8a..459e933 100644
--- a/content/browser/renderer_host/render_process_host_impl.cc
+++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -966,7 +966,7 @@
     // at this stage.
     child_process_launcher_.reset(new ChildProcessLauncher(
         new RendererSandboxedProcessLauncherDelegate(channel_.get()), cmd_line,
-        GetID(), this, child_token_,
+        GetID(), this, field_trial_state_.get(), child_token_,
         base::Bind(&RenderProcessHostImpl::OnMojoError, id_)));
     channel_->Pause();
 
@@ -1579,7 +1579,7 @@
 }
 
 void RenderProcessHostImpl::AppendRendererCommandLine(
-    base::CommandLine* command_line) const {
+    base::CommandLine* command_line) {
   // Pass the process type first, so it shows first in process listings.
   command_line->AppendSwitchASCII(switches::kProcessType,
                                   switches::kRendererProcess);
@@ -1618,7 +1618,7 @@
 
 void RenderProcessHostImpl::PropagateBrowserCommandLineToRenderer(
     const base::CommandLine& browser_cmd,
-    base::CommandLine* renderer_cmd) const {
+    base::CommandLine* renderer_cmd) {
   // Propagate the following switches to the renderer command line (along
   // with any associated values) if present in the browser command line.
   static const char* const kSwitchNames[] = {
@@ -1828,7 +1828,8 @@
   renderer_cmd->CopySwitchesFrom(browser_cmd, kSwitchNames,
                                  arraysize(kSwitchNames));
 
-  BrowserChildProcessHostImpl::CopyFeatureAndFieldTrialFlags(renderer_cmd);
+  field_trial_state_ =
+      BrowserChildProcessHostImpl::CopyFeatureAndFieldTrialFlags(renderer_cmd);
 
   if (browser_cmd.HasSwitch(switches::kTraceStartup) &&
       BrowserMainLoop::GetInstance()->is_tracing_startup_for_duration()) {
diff --git a/content/browser/renderer_host/render_process_host_impl.h b/content/browser/renderer_host/render_process_host_impl.h
index f07c487e..f97cd70a 100644
--- a/content/browser/renderer_host/render_process_host_impl.h
+++ b/content/browser/renderer_host/render_process_host_impl.h
@@ -336,14 +336,14 @@
 
   // Generates a command line to be used to spawn a renderer and appends the
   // results to |*command_line|.
-  void AppendRendererCommandLine(base::CommandLine* command_line) const;
+  void AppendRendererCommandLine(base::CommandLine* command_line);
 
   // Copies applicable command line switches from the given |browser_cmd| line
   // flags to the output |renderer_cmd| line flags. Not all switches will be
   // copied over.
   void PropagateBrowserCommandLineToRenderer(
       const base::CommandLine& browser_cmd,
-      base::CommandLine* renderer_cmd) const;
+      base::CommandLine* renderer_cmd);
 
   // Inspects the current object state and sets/removes background priority if
   // appropriate. Should be called after any of the involved data members
@@ -558,6 +558,12 @@
   // The memory allocator, if any, in which the renderer will write its metrics.
   std::unique_ptr<base::SharedPersistentMemoryAllocator> metrics_allocator_;
 
+  // Anonymous shared memory segment to share with subprocess containing list of
+  // field trials (represented as a string).
+  // TODO(crbug.com/653874): Eventually remove this and use single shared memory
+  // object across processes.
+  std::unique_ptr<base::SharedMemory> field_trial_state_;
+
   bool channel_connected_;
   bool sent_render_process_ready_;
 
diff --git a/content/browser/utility_process_host_impl.cc b/content/browser/utility_process_host_impl.cc
index 708a09d..8601a09 100644
--- a/content/browser/utility_process_host_impl.cc
+++ b/content/browser/utility_process_host_impl.cc
@@ -340,13 +340,10 @@
       cmd_line->AppendSwitch(switches::kUtilityProcessRunningElevated);
 #endif
 
-    process_->Launch(
-        new UtilitySandboxedProcessLauncherDelegate(exposed_dir_,
-                                                    run_elevated_,
-                                                    no_sandbox_, env_,
-                                                    process_->GetHost()),
-        cmd_line,
-        true);
+    process_->Launch(new UtilitySandboxedProcessLauncherDelegate(
+                         exposed_dir_, run_elevated_, no_sandbox_, env_,
+                         process_->GetHost()),
+                     cmd_line, nullptr, true);
   }
 
   return true;
diff --git a/content/public/browser/browser_child_process_host.h b/content/public/browser/browser_child_process_host.h
index e0f2d49e..e8506d8 100644
--- a/content/public/browser/browser_child_process_host.h
+++ b/content/public/browser/browser_child_process_host.h
@@ -6,6 +6,7 @@
 #define CONTENT_PUBLIC_BROWSER_BROWSER_CHILD_PROCESS_HOST_H_
 
 #include "base/environment.h"
+#include "base/memory/shared_memory.h"
 #include "base/process/kill.h"
 #include "base/process/process_handle.h"
 #include "base/strings/string16.h"
@@ -62,11 +63,13 @@
   ~BrowserChildProcessHost() override {}
 
   // Derived classes call this to launch the child process asynchronously.
-  // Takes ownership of |cmd_line| and |delegate|.
-  virtual void Launch(
-      SandboxedProcessLauncherDelegate* delegate,
-      base::CommandLine* cmd_line,
-      bool terminate_on_shutdown) = 0;
+  // Takes ownership of |cmd_line| and |delegate|. Takes in |field_trial_state|
+  // to explicitly pass its handle to be inherited on Windows to the child
+  // process via the command line.
+  virtual void Launch(SandboxedProcessLauncherDelegate* delegate,
+                      base::CommandLine* cmd_line,
+                      const base::SharedMemory* field_trial_state,
+                      bool terminate_on_shutdown) = 0;
 
   virtual const ChildProcessData& GetData() const = 0;
 
diff --git a/content/public/common/content_switches.cc b/content/public/common/content_switches.cc
index 41c636f0..95d7544 100644
--- a/content/public/common/content_switches.cc
+++ b/content/public/common/content_switches.cc
@@ -530,6 +530,12 @@
 // numbers.
 const char kExplicitlyAllowedPorts[]        = "explicitly-allowed-ports";
 
+// Handle to the shared memory segment containing field trial state that is to
+// be shared between processes. The argument to this switch is the handle id
+// (pointer on Windows) as a string, followed by a comma, then the size of the
+// shared memory segment as a string.
+const char kFieldTrialHandle[] = "field-trial-handle";
+
 // Always use the Skia GPU backend for drawing layer tiles. Only valid with GPU
 // accelerated compositing + impl-side painting. Overrides the
 // kEnableGpuRasterization flag.
diff --git a/content/public/common/content_switches.h b/content/public/common/content_switches.h
index dd1bc05..d6f16f47 100644
--- a/content/public/common/content_switches.h
+++ b/content/public/common/content_switches.h
@@ -162,6 +162,7 @@
 CONTENT_EXPORT extern const char kEnableWebVR[];
 CONTENT_EXPORT extern const char kEnableZeroCopy[];
 CONTENT_EXPORT extern const char kExplicitlyAllowedPorts[];
+CONTENT_EXPORT extern const char kFieldTrialHandle[];
 CONTENT_EXPORT extern const char kForceDisplayList2dCanvas[];
 CONTENT_EXPORT extern const char kForceGpuRasterization[];
 CONTENT_EXPORT extern const char kForceOverlayFullscreenVideo[];