blob: 2729eba3ae0ddf196d05d2c5d4c99e2f68dd2824 [file] [log] [blame]
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_EXTENSIONS_API_MESSAGING_LAUNCH_CONTEXT_H_
#define CHROME_BROWSER_EXTENSIONS_API_MESSAGING_LAUNCH_CONTEXT_H_
#include <stdint.h>
#include <memory>
#include <optional>
#include <string>
#include "base/files/file_path.h"
#include "base/files/platform_file.h"
#include "base/functional/callback.h"
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "base/process/process.h"
#include "base/sequence_checker.h"
#include "build/build_config.h"
#include "chrome/browser/extensions/api/messaging/native_process_launcher.h"
#include "url/gurl.h"
#if BUILDFLAG(IS_WIN)
#include "base/win/object_watcher.h"
#endif
namespace base {
class CommandLine;
class TaskRunner;
} // namespace base
namespace net {
class FileStream;
} // namespace net
namespace extensions {
// The state for a single native messaging host process launch. Instances live
// and die on the IO thread. Asynchronous process launch is initiated via
// `Start`. A consumer may cancel an in-progress launch by deleting the instance
// on the IO thread.
class LaunchContext
#if BUILDFLAG(IS_WIN)
: public base::win::ObjectWatcher::Delegate
#endif
{
public:
// `callback` is guaranteed not to be run after the returned instance is
// destroyed.
static std::unique_ptr<LaunchContext> Start(
bool allow_user_level_hosts,
bool require_native_initiated_connections,
bool native_hosts_executables_launch_directly,
intptr_t window_handle,
base::FilePath profile_directory,
std::string connect_id,
std::string error_arg,
GURL origin,
std::string native_host_name,
scoped_refptr<base::TaskRunner> background_task_runner,
NativeProcessLauncher::LaunchedCallback callback);
#if BUILDFLAG(IS_WIN)
~LaunchContext() override;
#else
~LaunchContext();
#endif
private:
LaunchContext(scoped_refptr<base::TaskRunner> background_task_runner,
NativeProcessLauncher::LaunchedCallback callback);
base::WeakPtr<LaunchContext> GetWeakPtr() {
return weak_ptr_factory_.GetWeakPtr();
}
// Returns the path to the manifest file for the native messaging host
// `host_name`. If `allow_user_level_hosts` is false, user-level manifests are
// ignored; otherwise, they are preferred over an all-users manifest. Returns
// an empty path if the host with the specified name cannot be found.
static base::FilePath FindManifest(const std::string& host_name,
bool allow_user_level_hosts,
std::string& error_message);
struct ProcessState {
ProcessState();
ProcessState(base::Process process,
base::ScopedPlatformFile read_file,
base::ScopedPlatformFile write_file);
ProcessState(ProcessState&& other) noexcept;
ProcessState& operator=(ProcessState&& other) noexcept;
~ProcessState();
// The child process.
base::Process process;
// The child's stdout.
base::ScopedPlatformFile read_file;
// The child's stdin.
base::ScopedPlatformFile write_file;
};
// Launches the native messaging process, providing it with one end each of a
// pair of pipes for its stdout and stdin. On Windows: if
// `native_hosts_executables_launch_directly` is true and the host is an .exe,
// the host is launched directly; otherwise, it is launched via cmd.exe. On
// success, returns the launched process and the out/in pipes.
static std::optional<ProcessState> LaunchNativeProcess(
const base::CommandLine& command_line,
bool native_hosts_executables_launch_directly);
// The result of a background process launch.
struct BackgroundLaunchResult {
explicit BackgroundLaunchResult(NativeProcessLauncher::LaunchResult result);
explicit BackgroundLaunchResult(ProcessState process_state);
BackgroundLaunchResult(BackgroundLaunchResult&& other) noexcept;
BackgroundLaunchResult& operator=(BackgroundLaunchResult&& other) noexcept;
~BackgroundLaunchResult();
// The result code of the launch.
NativeProcessLauncher::LaunchResult result;
// The handles for the child process, present only when `result` is
// `RESULT_SUCCESS`.
std::optional<ProcessState> process_state;
};
// Reads and validates the host's manifest, forms its command line, and
// launches it. Returns an error code or the process's handles.
static BackgroundLaunchResult LaunchInBackground(
bool allow_user_level_hosts,
bool require_native_initiated_connections,
bool native_hosts_executables_launch_directly,
intptr_t window_handle,
const base::FilePath& profile_directory,
const std::string& connect_id,
const std::string& error_arg,
const GURL& origin,
const std::string& native_host_name);
// Continues processing on the IO thread following `LaunchInBackground`. If
// the launch was cancelled (via deletion of the context), the host process
// is terminated. Otherwise, either the caller's callback is run with the
// failure code, or the pipes are connected.
static void OnProcessLaunched(base::WeakPtr<LaunchContext> weak_this,
BackgroundLaunchResult result);
// Connects to the host process.
void ConnectPipes(base::ScopedPlatformFile read_file,
base::ScopedPlatformFile write_file);
#if BUILDFLAG(IS_WIN)
// These methods are only needed on Windows, where an extra step is needed to
// connect to the named pipes used for stdin/stdout of the native messaging
// host process. The connections are established asynchronously via the IO
// completion port monitored by the IO thread.
// Handles the result of connecting to the host's stdout pipe.
void OnReadStreamConnectResult(int net_error);
// Handles the result of connecting to the host's stdin pipe.
void OnWriteStreamConnectResult(int net_error);
// Continues processing once a pipe has connected.
void OnPipeConnected();
// base::win::ObjectWatcher::Delegate:
// Handles unexpected termination of the host process.
void OnObjectSignaled(HANDLE object) override;
#endif // BUILDFLAG(IS_WIN)
// Reports success via the caller's callback, which may destroy `this`.
void OnSuccess(base::PlatformFile read_file,
std::unique_ptr<net::FileStream> read_stream,
std::unique_ptr<net::FileStream> write_stream);
// Reports failure via the caller's callback, which may destroy `this`.
void OnFailure(NativeProcessLauncher::LaunchResult launch_result);
scoped_refptr<base::TaskRunner> background_task_runner_;
NativeProcessLauncher::LaunchedCallback callback_;
base::Process native_process_;
#if BUILDFLAG(IS_WIN)
std::unique_ptr<net::FileStream> read_stream_;
std::unique_ptr<net::FileStream> write_stream_;
base::win::ObjectWatcher process_watcher_;
bool read_pipe_connected_ = false;
bool write_pipe_connected_ = false;
#endif // BUILDFLAG(IS_WIN)
SEQUENCE_CHECKER(sequence_checker_);
base::WeakPtrFactory<LaunchContext> weak_ptr_factory_{this};
};
} // namespace extensions
#endif // CHROME_BROWSER_EXTENSIONS_API_MESSAGING_LAUNCH_CONTEXT_H_