From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: deepak1556 <hop2deep@gmail.com>
Date: Wed, 17 Aug 2022 22:04:47 +0900
Subject: feat: configure launch options for service process

- POSIX:
  Allows configuring base::LaunchOptions::fds_to_remap when launching the child process.
- Win:
  Allows configuring base::LaunchOptions::handles_to_inherit, base::LaunchOptions::stdout_handle
  and base::LaunchOptions::stderr_handle when launching the child process.
- All:
  Allows configuring base::LauncOptions::current_directory, base::LaunchOptions::enviroment
  and base::LaunchOptions::clear_environment.

An example use of this option, UtilityProcess API allows reading the output From
stdout and stderr of child process by creating a pipe, whose write end is remapped
to STDOUT_FILENO/STD_OUTPUT_HANDLE and STDERR_FILENO/STD_ERROR_HANDLE allowing the
parent process to read from the pipe.

diff --git a/content/browser/child_process_launcher.h b/content/browser/child_process_launcher.h
index 7d449854cb60c184baba2cc17ae3d47fba3d3f7a..b76d963a0232fdbc27ddc52598860b35b6853034 100644
--- a/content/browser/child_process_launcher.h
+++ b/content/browser/child_process_launcher.h
@@ -33,6 +33,7 @@
 
 #if BUILDFLAG(IS_WIN)
 #include "base/win/windows_types.h"
+#include "base/win/scoped_handle.h"
 #endif
 
 #if BUILDFLAG(IS_POSIX)
@@ -166,7 +167,10 @@ struct ChildProcessLauncherFileData {
       delete;
   ~ChildProcessLauncherFileData();
 
-#if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_MAC)
+#if BUILDFLAG(IS_WIN)
+  base::win::ScopedHandle stdout_handle;
+  base::win::ScopedHandle stderr_handle;
+#elif BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_MAC)
   // Files opened by the browser and passed as corresponding file descriptors
   // in the child process. If a FilePath is provided, the file will be opened
   // and the descriptor cached for future process launches. If a ScopedFD is
@@ -181,6 +185,15 @@ struct ChildProcessLauncherFileData {
   std::map<std::string, absl::variant<base::FilePath, base::ScopedFD>>
       files_to_preload;
 #endif
+
+#if BUILDFLAG(IS_POSIX)
+  // Map of file descriptors to pass. This is used instead of
+  // `files_to_preload` when the data needs to be installed at an exact FD
+  // number in the new process.
+  //
+  // Currently only supported on POSIX platforms.
+  std::map<int, base::ScopedFD> additional_remapped_fds;
+#endif
 };
 
 // Launches a process asynchronously and notifies the client of the process
diff --git a/content/browser/child_process_launcher_helper_linux.cc b/content/browser/child_process_launcher_helper_linux.cc
index 795ad47165f2f02e816f424ce74391a435c61a32..ace57135682f4372814acfbd202d1b077ecb1c0c 100644
--- a/content/browser/child_process_launcher_helper_linux.cc
+++ b/content/browser/child_process_launcher_helper_linux.cc
@@ -63,6 +63,11 @@ bool ChildProcessLauncherHelper::BeforeLaunchOnLauncherThread(
       options->fds_to_remap.emplace_back(sandbox_fd, GetSandboxFD());
     }
 
+    for (const auto& remapped_fd : file_data_->additional_remapped_fds) {
+      options->fds_to_remap.emplace_back(remapped_fd.second.get(),
+                                         remapped_fd.first);
+    }
+
     // (For Electron), if we're launching without zygote, that means we're
     // launching an unsandboxed process (since all sandboxed processes are
     // forked from the zygote). Relax the allow_new_privs option to permit
@@ -72,7 +77,9 @@ bool ChildProcessLauncherHelper::BeforeLaunchOnLauncherThread(
       options->allow_new_privs = true;
     }
 
+    options->current_directory = delegate_->GetCurrentDirectory();
     options->environment = delegate_->GetEnvironment();
+    options->clear_environment = !delegate_->ShouldInheritEnvironment();
   } else {
     DCHECK(GetZygoteForLaunch());
     // Environment variables could be supported in the future, but are not
diff --git a/content/browser/child_process_launcher_helper_mac.cc b/content/browser/child_process_launcher_helper_mac.cc
index 8736af0021dccba915e3d2303191ae3ec80f6e75..56f8384c3ff4959272363cb71e4c380ee9125273 100644
--- a/content/browser/child_process_launcher_helper_mac.cc
+++ b/content/browser/child_process_launcher_helper_mac.cc
@@ -123,7 +123,8 @@ bool ChildProcessLauncherHelper::BeforeLaunchOnLauncherThread(
       'mojo', base::MachRendezvousPort(endpoint.TakeMachReceiveRight())));
 
   options->environment = delegate_->GetEnvironment();
-
+  options->clear_environment = !delegate_->ShouldInheritEnvironment();
+  options->current_directory = delegate_->GetCurrentDirectory();
   options->disclaim_responsibility = delegate_->DisclaimResponsibility();
   options->enable_cpu_security_mitigations =
       delegate_->EnableCpuSecurityMitigations();
@@ -187,6 +188,11 @@ bool ChildProcessLauncherHelper::BeforeLaunchOnLauncherThread(
         base::StringPrintf("%s%d", sandbox::switches::kSeatbeltClient, pipe));
   }
 
+  for (const auto& remapped_fd : file_data_->additional_remapped_fds) {
+    options->fds_to_remap.emplace_back(remapped_fd.second.get(),
+                                       remapped_fd.first);
+  }
+
   return true;
 }
 
diff --git a/content/browser/child_process_launcher_helper_win.cc b/content/browser/child_process_launcher_helper_win.cc
index 220eab3370bb2632150f845d29ab7babf44472f5..6f055560ecfe480de1c7c8f4f2c22a5977043b9c 100644
--- a/content/browser/child_process_launcher_helper_win.cc
+++ b/content/browser/child_process_launcher_helper_win.cc
@@ -25,6 +25,8 @@
 #include "mojo/public/cpp/platform/platform_channel.h"
 #include "sandbox/win/src/sandbox_types.h"
 
+#include <windows.h>
+
 namespace {
 
 // Helper to avoid marking the log file as non-executable every time we launch a
@@ -259,6 +261,30 @@ bool ChildProcessLauncherHelper::BeforeLaunchOnLauncherThread(
     mojo_channel_->PrepareToPassRemoteEndpoint(&options->handles_to_inherit,
                                                command_line());
   }
+
+  if (file_data_->stdout_handle.IsValid() || file_data_->stderr_handle.IsValid()) {
+    // base::LaunchProcess requires that if any of the stdio handle is customized then
+    // the other two handles should also be set.
+    // https://source.chromium.org/chromium/chromium/src/+/main:base/process/launch_win.cc;l=341-350
+    options->stdin_handle = INVALID_HANDLE_VALUE;
+    if (file_data_->stdout_handle.IsValid()) {
+      options->stdout_handle = file_data_->stdout_handle.get();
+    } else {
+      options->stdout_handle = GetStdHandle(STD_OUTPUT_HANDLE);
+    }
+
+    if (file_data_->stderr_handle.IsValid()) {
+      options->stderr_handle = file_data_->stderr_handle.get();
+    } else {
+      options->stderr_handle = GetStdHandle(STD_ERROR_HANDLE);
+    }
+    options->handles_to_inherit.push_back(options->stdout_handle);
+    options->handles_to_inherit.push_back(options->stderr_handle);
+  }
+
+  options->current_directory = delegate_->GetCurrentDirectory();
+  options->environment = delegate_->GetEnvironment();
+  options->clear_environment = !delegate_->ShouldInheritEnvironment();
   return true;
 }
 
@@ -286,7 +312,7 @@ ChildProcessLauncherHelper::LaunchProcessOnLauncherThread(
   ChildProcessLauncherHelper::Process process;
   *launch_result =
       StartSandboxedProcess(delegate_.get(), *command_line(),
-                            options->handles_to_inherit, &process.process);
+                            options, &process.process);
   return process;
 }
 
diff --git a/content/browser/service_process_host_impl.cc b/content/browser/service_process_host_impl.cc
index 1987b9539e6d393334e0a9b1e4145fa816e14559..73d8c2fcbed9db89161ad3fabd5cbfb6b3761a4d 100644
--- a/content/browser/service_process_host_impl.cc
+++ b/content/browser/service_process_host_impl.cc
@@ -207,6 +207,16 @@ void LaunchServiceProcess(mojo::GenericPendingReceiver receiver,
       options.allow_gpu_client.value()) {
     host->SetAllowGpuClient();
   }
+
+#if BUILDFLAG(IS_WIN)
+  host->SetStdioHandles(std::move(options.stdout_handle), std::move(options.stderr_handle));
+#elif BUILDFLAG(IS_POSIX)
+  host->SetAdditionalFds(std::move(options.fds_to_remap));
+#endif
+  host->SetCurrentDirectory(options.current_directory);
+  host->SetEnv(options.environment);
+  if (options.clear_environment)
+    host->ClearEnvironment();
   host->Start();
   host->GetChildProcess()->BindServiceInterface(std::move(receiver));
 }
diff --git a/content/browser/utility_process_host.cc b/content/browser/utility_process_host.cc
index 3cca937cc1af689df506bce8f31baee7968e7798..9b99a3caf366368917c39ae5c12a11f5294c3948 100644
--- a/content/browser/utility_process_host.cc
+++ b/content/browser/utility_process_host.cc
@@ -178,11 +178,13 @@ const ChildProcessData& UtilityProcessHost::GetData() {
   return process_->GetData();
 }
 
-#if BUILDFLAG(IS_POSIX)
 void UtilityProcessHost::SetEnv(const base::EnvironmentMap& env) {
   env_ = env;
 }
-#endif
+
+void UtilityProcessHost::ClearEnvironment() {
+  inherit_environment_ = false;
+}
 
 bool UtilityProcessHost::Start() {
   return StartProcess();
@@ -254,6 +256,24 @@ void UtilityProcessHost::SetZygoteForTesting(ZygoteCommunication* handle) {
 }
 #endif  // BUILDFLAG(USE_ZYGOTE)
 
+#if BUILDFLAG(IS_WIN)
+void UtilityProcessHost::SetStdioHandles(
+    base::win::ScopedHandle stdout_handle,
+    base::win::ScopedHandle stderr_handle) {
+  stdout_handle_ = std::move(stdout_handle);
+  stderr_handle_ = std::move(stderr_handle);
+}
+#elif BUILDFLAG(IS_POSIX)
+void UtilityProcessHost::SetAdditionalFds(base::FileHandleMappingVector mapping) {
+  fds_to_remap_ = std::move(mapping);
+}
+#endif
+
+void UtilityProcessHost::SetCurrentDirectory(
+    const base::FilePath& cwd) {
+  current_directory_ = cwd;
+}
+
 mojom::ChildProcess* UtilityProcessHost::GetChildProcess() {
   return static_cast<ChildProcessHostImpl*>(process_->GetHost())
       ->child_process();
@@ -463,9 +483,22 @@ bool UtilityProcessHost::StartProcess() {
     }
 #endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_ASH)
 
+#if BUILDFLAG(IS_WIN)
+    file_data_->stdout_handle = std::move(stdout_handle_);
+    file_data_->stderr_handle = std::move(stderr_handle_);
+#elif BUILDFLAG(IS_POSIX)
+    if (!fds_to_remap_.empty()) {
+      for (const auto& remapped_fd : fds_to_remap_) {
+        file_data_->additional_remapped_fds.emplace(
+            remapped_fd.second, remapped_fd.first);
+      }
+    }
+#endif
+
     std::unique_ptr<UtilitySandboxedProcessLauncherDelegate> delegate =
         std::make_unique<UtilitySandboxedProcessLauncherDelegate>(
-            sandbox_type_, env_, *cmd_line);
+            sandbox_type_, env_, current_directory_, *cmd_line,
+            inherit_environment_);
 
 #if BUILDFLAG(IS_WIN)
     if (!preload_libraries_.empty()) {
diff --git a/content/browser/utility_process_host.h b/content/browser/utility_process_host.h
index cba1933af784da5e3afd12b7695a734158a5db1b..ecb0b0e02870386f3ad4365461325ba0627ba1ce 100644
--- a/content/browser/utility_process_host.h
+++ b/content/browser/utility_process_host.h
@@ -35,6 +35,10 @@
 #include "mojo/public/cpp/system/message_pipe.h"
 #endif
 
+#if BUILDFLAG(IS_WIN)
+#include "base/win/scoped_handle.h"
+#endif
+
 namespace base {
 class Thread;
 }  // namespace base
@@ -104,9 +108,13 @@ class CONTENT_EXPORT UtilityProcessHost
 
   // Returns information about the utility child process.
   const ChildProcessData& GetData();
-#if BUILDFLAG(IS_POSIX)
+
+  // Set/Unset environment variables.
   void SetEnv(const base::EnvironmentMap& env);
-#endif
+
+  // Clear the environment for the new process before processing
+  // changes from SetEnv.
+  void ClearEnvironment();
 
   // Starts the utility process.
   bool Start();
@@ -157,6 +165,16 @@ class CONTENT_EXPORT UtilityProcessHost
   void SetZygoteForTesting(ZygoteCommunication* handle);
 #endif  // BUILDFLAG(USE_ZYGOTE)
 
+#if BUILDFLAG(IS_WIN)
+  void SetStdioHandles(base::win::ScopedHandle stdout_handle,
+                       base::win::ScopedHandle stderr_handle);
+#elif BUILDFLAG(IS_POSIX)
+  void SetAdditionalFds(base::FileHandleMappingVector mapping);
+#endif
+
+  // Sets the working directory of the process.
+  void SetCurrentDirectory(const base::FilePath& cwd);
+
   // Returns a control interface for the running child process.
   mojom::ChildProcess* GetChildProcess();
 
@@ -212,6 +230,22 @@ class CONTENT_EXPORT UtilityProcessHost
   std::optional<raw_ptr<ZygoteCommunication>> zygote_for_testing_;
 #endif  // BUILDFLAG(USE_ZYGOTE)
 
+#if BUILDFLAG(IS_WIN)
+  // Specifies the handles for redirection of stdout and stderr.
+  base::win::ScopedHandle stdout_handle_;
+  base::win::ScopedHandle stderr_handle_;
+#elif BUILDFLAG(IS_POSIX)
+  // Specifies file descriptors to propagate into the child process
+  // based on the mapping.
+  base::FileHandleMappingVector fds_to_remap_;
+#endif
+
+  // If not empty, change to this directory before executing the new process.
+  base::FilePath current_directory_;
+
+  // Inherit enviroment from parent process.
+  bool inherit_environment_ = true;
+
   // Indicates whether the process has been successfully launched yet, or if
   // launch failed.
   enum class LaunchState {
diff --git a/content/browser/utility_sandbox_delegate.cc b/content/browser/utility_sandbox_delegate.cc
index 086089dc5929cb98be9423a6c2a3a0c40876c8eb..4cd54d37f6480558b49a87a9fe3bfb2b71e3edd9 100644
--- a/content/browser/utility_sandbox_delegate.cc
+++ b/content/browser/utility_sandbox_delegate.cc
@@ -29,13 +29,15 @@ UtilitySandboxedProcessLauncherDelegate::
     UtilitySandboxedProcessLauncherDelegate(
         sandbox::mojom::Sandbox sandbox_type,
         const base::EnvironmentMap& env,
-        const base::CommandLine& cmd_line)
+        const base::FilePath& cwd,
+        const base::CommandLine& cmd_line,
+        bool inherit_environment)
     :
-#if BUILDFLAG(IS_POSIX)
       env_(env),
-#endif
+      current_directory_(cwd),
       sandbox_type_(sandbox_type),
-      cmd_line_(cmd_line) {
+      cmd_line_(cmd_line),
+      inherit_environment_(inherit_environment) {
 #if DCHECK_IS_ON()
   bool supported_sandbox_type =
       sandbox_type_ == sandbox::mojom::Sandbox::kNoSandbox ||
@@ -97,11 +99,17 @@ UtilitySandboxedProcessLauncherDelegate::GetSandboxType() {
   return sandbox_type_;
 }
 
-#if BUILDFLAG(IS_POSIX)
 base::EnvironmentMap UtilitySandboxedProcessLauncherDelegate::GetEnvironment() {
   return env_;
 }
-#endif  // BUILDFLAG(IS_POSIX)
+
+bool UtilitySandboxedProcessLauncherDelegate::ShouldInheritEnvironment() {
+  return inherit_environment_;
+}
+
+base::FilePath UtilitySandboxedProcessLauncherDelegate::GetCurrentDirectory() {
+  return current_directory_;
+}
 
 #if BUILDFLAG(USE_ZYGOTE)
 ZygoteCommunication* UtilitySandboxedProcessLauncherDelegate::GetZygote() {
diff --git a/content/browser/utility_sandbox_delegate.h b/content/browser/utility_sandbox_delegate.h
index fc3f310c05343662063939789e2d1442aa7ef29b..068494a6e6375cfc72a108fbb5476f68308995fd 100644
--- a/content/browser/utility_sandbox_delegate.h
+++ b/content/browser/utility_sandbox_delegate.h
@@ -29,7 +29,9 @@ class UtilitySandboxedProcessLauncherDelegate
  public:
   UtilitySandboxedProcessLauncherDelegate(sandbox::mojom::Sandbox sandbox_type,
                                           const base::EnvironmentMap& env,
-                                          const base::CommandLine& cmd_line);
+                                          const base::FilePath& cwd,
+                                          const base::CommandLine& cmd_line,
+                                          bool inherit_environment);
   ~UtilitySandboxedProcessLauncherDelegate() override;
 
   sandbox::mojom::Sandbox GetSandboxType() override;
@@ -56,18 +58,16 @@ class UtilitySandboxedProcessLauncherDelegate
   ZygoteCommunication* GetZygote() override;
 #endif  // BUILDFLAG(USE_ZYGOTE)
 
-#if BUILDFLAG(IS_POSIX)
   base::EnvironmentMap GetEnvironment() override;
-#endif  // BUILDFLAG(IS_POSIX)
+  bool ShouldInheritEnvironment() override;
+  base::FilePath GetCurrentDirectory() override;
 
 #if BUILDFLAG(USE_ZYGOTE)
   void SetZygote(ZygoteCommunication* handle);
 #endif  // BUILDFLAG(USE_ZYGOTE_HANDLE)
 
  private:
-#if BUILDFLAG(IS_POSIX)
   base::EnvironmentMap env_;
-#endif  // BUILDFLAG(IS_POSIX)
 
 #if BUILDFLAG(IS_WIN)
   std::vector<base::FilePath> preload_libraries_;
@@ -78,8 +78,10 @@ class UtilitySandboxedProcessLauncherDelegate
   std::optional<raw_ptr<ZygoteCommunication>> zygote_;
 #endif  // BUILDFLAG(USE_ZYGOTE)
 
+  base::FilePath current_directory_;
   sandbox::mojom::Sandbox sandbox_type_;
   base::CommandLine cmd_line_;
+  bool inherit_environment_;
 };
 }  // namespace content
 
diff --git a/content/common/sandbox_init_win.cc b/content/common/sandbox_init_win.cc
index 498f60227d13eb2e476413f88eaa58cc0babf461..619639ad5d22a1121b0e0d5f2c9e3c10394cdbd7 100644
--- a/content/common/sandbox_init_win.cc
+++ b/content/common/sandbox_init_win.cc
@@ -23,7 +23,7 @@ namespace content {
 sandbox::ResultCode StartSandboxedProcess(
     SandboxedProcessLauncherDelegate* delegate,
     const base::CommandLine& target_command_line,
-    const base::HandlesToInheritVector& handles_to_inherit,
+    const base::LaunchOptions* options,
     base::Process* process) {
   std::string type_str =
       target_command_line.GetSwitchValueASCII(switches::kProcessType);
@@ -45,7 +45,7 @@ sandbox::ResultCode StartSandboxedProcess(
   }
 
   return sandbox::policy::SandboxWin::StartSandboxedProcess(
-      full_command_line, type_str, handles_to_inherit, delegate, process);
+      full_command_line, type_str, options, delegate, process);
 }
 
 }  // namespace content
diff --git a/content/public/browser/service_process_host.cc b/content/public/browser/service_process_host.cc
index 556d27b78e6b2bf96c9951cee6bda323ecca675e..7ba6b859e8c27da7f267e988efaac1d54f8b57b8 100644
--- a/content/public/browser/service_process_host.cc
+++ b/content/public/browser/service_process_host.cc
@@ -52,12 +52,45 @@ ServiceProcessHost::Options::WithExtraCommandLineSwitches(
   return *this;
 }
 
+#if BUILDFLAG(IS_WIN)
+ServiceProcessHost::Options& ServiceProcessHost::Options::WithStdoutHandle(
+    base::win::ScopedHandle handle) {
+  stdout_handle = std::move(handle);
+  return *this;
+}
+
+ServiceProcessHost::Options& ServiceProcessHost::Options::WithStderrHandle(
+    base::win::ScopedHandle handle) {
+  stderr_handle = std::move(handle);
+  return *this;
+}
+#elif BUILDFLAG(IS_POSIX)
+ServiceProcessHost::Options& ServiceProcessHost::Options::WithAdditionalFds(
+    base::FileHandleMappingVector mapping) {
+  fds_to_remap = std::move(mapping);
+  return *this;
+}
+#endif
+
 ServiceProcessHost::Options& ServiceProcessHost::Options::WithProcessCallback(
     base::OnceCallback<void(const base::Process&)> callback) {
   process_callback = std::move(callback);
   return *this;
 }
 
+ServiceProcessHost::Options& ServiceProcessHost::Options::WithCurrentDirectory(
+    const base::FilePath& cwd) {
+  current_directory = cwd;
+  return *this;
+}
+
+ServiceProcessHost::Options& ServiceProcessHost::Options::WithEnvironment(
+    const base::EnvironmentMap& env, bool new_environment) {
+  environment = env;
+  clear_environment = new_environment;
+  return *this;
+}
+
 #if BUILDFLAG(IS_WIN)
 ServiceProcessHost::Options&
 ServiceProcessHost::Options::WithPreloadedLibraries(
diff --git a/content/public/browser/service_process_host.h b/content/public/browser/service_process_host.h
index acac8d4bf5e256e5c05e8c7c9592f9857d5a493d..4895ba5c305c898bb21472a2408ecd62afb46fd6 100644
--- a/content/public/browser/service_process_host.h
+++ b/content/public/browser/service_process_host.h
@@ -14,6 +14,7 @@
 #include "base/command_line.h"
 #include "base/functional/callback.h"
 #include "base/observer_list_types.h"
+#include "base/process/launch.h"
 #include "base/process/process_handle.h"
 #include "base/strings/string_piece.h"
 #include "content/common/content_export.h"
@@ -29,6 +30,10 @@
 #include "base/types/pass_key.h"
 #endif  // BUILDFLAG(IS_WIN)
 
+#if BUILDFLAG(IS_WIN)
+#include "base/win/scoped_handle.h"
+#endif
+
 namespace base {
 class Process;
 }  // namespace base
@@ -96,11 +101,30 @@ class CONTENT_EXPORT ServiceProcessHost {
     // Specifies extra command line switches to append before launch.
     Options& WithExtraCommandLineSwitches(std::vector<std::string> switches);
 
+#if BUILDFLAG(IS_WIN)
+    // Specifies the handles for redirection of stdout and stderr.
+    Options& WithStdoutHandle(base::win::ScopedHandle stdout_handle);
+    Options& WithStderrHandle(base::win::ScopedHandle stderr_handle);
+#elif BUILDFLAG(IS_POSIX)
+    // Specifies file descriptors to propagate into the child process
+    // based on the mapping.
+    Options& WithAdditionalFds(base::FileHandleMappingVector mapping);
+#endif
+
     // Specifies a callback to be invoked with service process once it's
     // launched. Will be on UI thread.
     Options& WithProcessCallback(
         base::OnceCallback<void(const base::Process&)>);
 
+    // Specifies the working directory for the launched process.
+    Options& WithCurrentDirectory(const base::FilePath& cwd);
+
+    // Specifies the environment that should be applied to the process.
+    // |new_environment| controls whether the process should inherit
+    // environment from the parent process.
+    Options& WithEnvironment(const base::EnvironmentMap& environment,
+                             bool new_environment);
+
 #if BUILDFLAG(IS_WIN)
     // Specifies libraries to preload before the sandbox is locked down. Paths
     // should be absolute paths. Libraries will be preloaded before sandbox
@@ -133,12 +157,21 @@ class CONTENT_EXPORT ServiceProcessHost {
     std::optional<GURL> site;
     std::optional<int> child_flags;
     std::vector<std::string> extra_switches;
+#if BUILDFLAG(IS_WIN)
+    base::win::ScopedHandle stdout_handle;
+    base::win::ScopedHandle stderr_handle;
+#elif BUILDFLAG(IS_POSIX)
+    base::FileHandleMappingVector fds_to_remap;
+#endif
     base::OnceCallback<void(const base::Process&)> process_callback;
 #if BUILDFLAG(IS_WIN)
     std::vector<base::FilePath> preload_libraries;
     std::optional<bool> pin_user32;
 #endif  // BUILDFLAG(IS_WIN)
     std::optional<bool> allow_gpu_client;
+    base::FilePath current_directory;
+    base::EnvironmentMap environment;
+    bool clear_environment = false;
   };
 
   // An interface which can be implemented and registered/unregistered with
diff --git a/content/public/common/sandbox_init_win.h b/content/public/common/sandbox_init_win.h
index 9bb4b30ba0f5d37ec2b28f0848d94f34c24f9423..b614fef01ee5cdf81b7112be721b851c454756a2 100644
--- a/content/public/common/sandbox_init_win.h
+++ b/content/public/common/sandbox_init_win.h
@@ -29,7 +29,7 @@ class SandboxedProcessLauncherDelegate;
 CONTENT_EXPORT sandbox::ResultCode StartSandboxedProcess(
     SandboxedProcessLauncherDelegate* delegate,
     const base::CommandLine& target_command_line,
-    const base::HandlesToInheritVector& handles_to_inherit,
+    const base::LaunchOptions* options,
     base::Process* process);
 
 }  // namespace content
diff --git a/content/public/common/sandboxed_process_launcher_delegate.cc b/content/public/common/sandboxed_process_launcher_delegate.cc
index 9c1aa450f32b6812d4a87cd0b9ee0dfb1a9557f4..3360302b4511ed914ac2d5756dcc7edf7e675631 100644
--- a/content/public/common/sandboxed_process_launcher_delegate.cc
+++ b/content/public/common/sandboxed_process_launcher_delegate.cc
@@ -68,11 +68,17 @@ ZygoteCommunication* SandboxedProcessLauncherDelegate::GetZygote() {
 }
 #endif  // BUILDFLAG(USE_ZYGOTE)
 
-#if BUILDFLAG(IS_POSIX)
 base::EnvironmentMap SandboxedProcessLauncherDelegate::GetEnvironment() {
   return base::EnvironmentMap();
 }
-#endif  // BUILDFLAG(IS_POSIX)
+
+bool SandboxedProcessLauncherDelegate::ShouldInheritEnvironment() {
+  return true;
+}
+
+base::FilePath SandboxedProcessLauncherDelegate::GetCurrentDirectory() {
+  return base::FilePath();
+}
 
 #if BUILDFLAG(IS_MAC)
 
diff --git a/content/public/common/sandboxed_process_launcher_delegate.h b/content/public/common/sandboxed_process_launcher_delegate.h
index cb43aa14c9742f3788ae58c3e49b890cd532f327..6a738f7aade504f2ff3bb6647a0da8f8d1933de2 100644
--- a/content/public/common/sandboxed_process_launcher_delegate.h
+++ b/content/public/common/sandboxed_process_launcher_delegate.h
@@ -6,6 +6,7 @@
 #define CONTENT_PUBLIC_COMMON_SANDBOXED_PROCESS_LAUNCHER_DELEGATE_H_
 
 #include "base/environment.h"
+#include "base/files/file_path.h"
 #include "base/files/scoped_file.h"
 #include "base/process/process.h"
 #include "build/build_config.h"
@@ -57,10 +58,14 @@ class CONTENT_EXPORT SandboxedProcessLauncherDelegate
   virtual ZygoteCommunication* GetZygote();
 #endif  // BUILDFLAG(USE_ZYGOTE)
 
-#if BUILDFLAG(IS_POSIX)
   // Override this if the process needs a non-empty environment map.
   virtual base::EnvironmentMap GetEnvironment();
-#endif  // BUILDFLAG(IS_POSIX)
+
+  // Override this if the process should not inherit parent environment.
+  virtual bool ShouldInheritEnvironment();
+
+  // Specifies the directory to change to before executing the process.
+  virtual base::FilePath GetCurrentDirectory();
 
 #if BUILDFLAG(IS_MAC)
   // Whether or not to disclaim TCC responsibility for the process, defaults to
diff --git a/sandbox/policy/win/sandbox_win.cc b/sandbox/policy/win/sandbox_win.cc
index d7eed71feab0b4cc2c7b772c92b19897a447c06b..047c70a256f0a144f4835f0cf9dee64113a93791 100644
--- a/sandbox/policy/win/sandbox_win.cc
+++ b/sandbox/policy/win/sandbox_win.cc
@@ -747,11 +747,9 @@ base::win::ScopedHandle CreateUnsandboxedJob() {
 // command line flag.
 ResultCode LaunchWithoutSandbox(
     const base::CommandLine& cmd_line,
-    const base::HandlesToInheritVector& handles_to_inherit,
+    base::LaunchOptions options,
     SandboxDelegate* delegate,
     base::Process* process) {
-  base::LaunchOptions options;
-  options.handles_to_inherit = handles_to_inherit;
   // Network process runs in a job even when unsandboxed. This is to ensure it
   // does not outlive the browser, which could happen if there is a lot of I/O
   // on process shutdown, in which case TerminateProcess can fail. See
@@ -975,7 +973,7 @@ bool SandboxWin::InitTargetServices(TargetServices* target_services) {
 ResultCode SandboxWin::GeneratePolicyForSandboxedProcess(
     const base::CommandLine& cmd_line,
     const std::string& process_type,
-    const base::HandlesToInheritVector& handles_to_inherit,
+    const base::LaunchOptions* options,
     SandboxDelegate* delegate,
     TargetPolicy* policy) {
   const base::CommandLine& launcher_process_command_line =
@@ -989,7 +987,7 @@ ResultCode SandboxWin::GeneratePolicyForSandboxedProcess(
   }
 
   // Add any handles to be inherited to the policy.
-  for (HANDLE handle : handles_to_inherit)
+  for (HANDLE handle : options->handles_to_inherit)
     policy->AddHandleToShare(handle);
 
   if (!policy->GetConfig()->IsConfigured()) {
@@ -1004,6 +1002,13 @@ ResultCode SandboxWin::GeneratePolicyForSandboxedProcess(
   // have no effect. These calls can fail with SBOX_ERROR_BAD_PARAMS.
   policy->SetStdoutHandle(GetStdHandle(STD_OUTPUT_HANDLE));
   policy->SetStderrHandle(GetStdHandle(STD_ERROR_HANDLE));
+#else
+  if (options->stdout_handle != nullptr && options->stdout_handle != INVALID_HANDLE_VALUE) {
+    policy->SetStdoutHandle(options->stdout_handle);
+  }
+  if (options->stderr_handle != nullptr && options->stderr_handle != INVALID_HANDLE_VALUE) {
+    policy->SetStderrHandle(options->stderr_handle);
+  }
 #endif
 
   if (!delegate->PreSpawnTarget(policy))
@@ -1016,7 +1021,7 @@ ResultCode SandboxWin::GeneratePolicyForSandboxedProcess(
 ResultCode SandboxWin::StartSandboxedProcess(
     const base::CommandLine& cmd_line,
     const std::string& process_type,
-    const base::HandlesToInheritVector& handles_to_inherit,
+    const base::LaunchOptions* options,
     SandboxDelegate* delegate,
     base::Process* process) {
   SandboxLaunchTimer timer;
@@ -1024,7 +1029,7 @@ ResultCode SandboxWin::StartSandboxedProcess(
   // Avoid making a policy if we won't use it.
   if (IsUnsandboxedProcess(delegate->GetSandboxType(), cmd_line,
                            *base::CommandLine::ForCurrentProcess())) {
-    return LaunchWithoutSandbox(cmd_line, handles_to_inherit, delegate,
+    return LaunchWithoutSandbox(cmd_line, *options, delegate,
                                 process);
   }
 
@@ -1032,7 +1037,7 @@ ResultCode SandboxWin::StartSandboxedProcess(
   timer.OnPolicyCreated();
 
   ResultCode result = GeneratePolicyForSandboxedProcess(
-      cmd_line, process_type, handles_to_inherit, delegate, policy.get());
+      cmd_line, process_type, options, delegate, policy.get());
   if (SBOX_ALL_OK != result)
     return result;
   timer.OnPolicyGenerated();
diff --git a/sandbox/policy/win/sandbox_win.h b/sandbox/policy/win/sandbox_win.h
index 54d808db3a0a2aff198e132fae02c8649a0b547e..e1f5321298e634a310afc10773b93fedbad22431 100644
--- a/sandbox/policy/win/sandbox_win.h
+++ b/sandbox/policy/win/sandbox_win.h
@@ -53,7 +53,7 @@ class SANDBOX_POLICY_EXPORT SandboxWin {
   static ResultCode StartSandboxedProcess(
       const base::CommandLine& cmd_line,
       const std::string& process_type,
-      const base::HandlesToInheritVector& handles_to_inherit,
+      const base::LaunchOptions* options,
       SandboxDelegate* delegate,
       base::Process* process);
 
@@ -67,7 +67,7 @@ class SANDBOX_POLICY_EXPORT SandboxWin {
   static ResultCode GeneratePolicyForSandboxedProcess(
       const base::CommandLine& cmd_line,
       const std::string& process_type,
-      const base::HandlesToInheritVector& handles_to_inherit,
+      const base::LaunchOptions* options,
       SandboxDelegate* delegate,
       TargetPolicy* policy);
 
