
This is a reland of 18905f7828
Reland changes: The change was reverted because
ChildProcessLancher::Notify was not called after a policy error,
causing the launch to hang. Now, the FinishStartSandboxedProcess
callback will always run ensuring that Notify will always run, too.
Also, a test was added to show that a failed launch will not hang.
Original change's description:
> Parallel process launching
>
> This change adds support for parallel process launching on Windows.
>
> Background: Processes are launched on a launcher thread, but this thread
> can become bottlenecked if launching multiple processes simultaneously.
> Most of the time spent in the launcher thread is observed to be in the
> CreateProcess call.
>
> This change makes process launching async by moving the target creation
> work to the thread pool. This frees up the launcher thread so it can
> accept tasks more quickly, and multiple target creations can now run in
> parallel.
>
> --enable-features=WinSboxParallelProcessLaunch
>
> Bug: 1499551
> Tests: sbox_integration_tests ParallelLaunchTest.*
> Change-Id: I7add6a6a68d31868a41fb433b223695ff36d8769
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5015584
> Reviewed-by: Will Harris <wfh@chromium.org>
> Commit-Queue: Ben Bamesberger <benb@microsoft.com>
> Reviewed-by: Alex Gough <ajgo@chromium.org>
> Reviewed-by: Caitlin Fischer <caitlinfischer@google.com>
> Reviewed-by: Bo Liu <boliu@chromium.org>
> Cr-Commit-Position: refs/heads/main@{#1348194}
content_browsertests
BrowserChildProcessObserverBrowserTest.LaunchPreSpawnFailed
Bug: 40287847
Change-Id: Ia200ec065a9540f9d4790e2c6e0ae98178fdf747
Tests: sbox_integration_tests ParallelLaunchTest.*,
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5852357
Reviewed-by: Mark Pearson <mpearson@chromium.org>
Reviewed-by: Will Harris <wfh@chromium.org>
Reviewed-by: Nasko Oskov <nasko@chromium.org>
Commit-Queue: Will Harris <wfh@chromium.org>
Reviewed-by: Alex Gough <ajgo@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1365065}
582 lines
19 KiB
C++
582 lines
19 KiB
C++
// Copyright 2022 The Chromium Authors
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#include "content/public/browser/browser_child_process_observer.h"
|
|
|
|
#include "base/functional/bind.h"
|
|
#include "base/run_loop.h"
|
|
#include "build/build_config.h"
|
|
#include "content/browser/browser_child_process_host_impl.h"
|
|
#include "content/browser/child_process_host_impl.h"
|
|
#include "content/browser/utility_process_host.h"
|
|
#include "content/public/browser/browser_child_process_host.h"
|
|
#include "content/public/browser/browser_child_process_host_delegate.h"
|
|
#include "content/public/browser/child_process_data.h"
|
|
#include "content/public/common/content_switches.h"
|
|
#include "content/public/common/process_type.h"
|
|
#include "content/public/common/sandboxed_process_launcher_delegate.h"
|
|
#include "content/public/test/browser_test.h"
|
|
#include "content/public/test/content_browser_test.h"
|
|
#include "content/public/test/test_service.mojom.h"
|
|
#include "sandbox/policy/sandbox_type.h"
|
|
#include "testing/gmock/include/gmock/gmock.h"
|
|
|
|
namespace content {
|
|
|
|
namespace {
|
|
|
|
// An enum that represent the different type of notitifcations that exist in
|
|
// BrowserChildProcessObserver.
|
|
enum class Notification {
|
|
kLaunchedAndConnected,
|
|
kDisconnected,
|
|
kCrashed,
|
|
kKilled,
|
|
kLaunchFailed,
|
|
kExitedNormally,
|
|
};
|
|
|
|
// Nicer test output.
|
|
std::ostream& operator<<(std::ostream& os, Notification notification) {
|
|
switch (notification) {
|
|
case Notification::kLaunchedAndConnected:
|
|
os << "LaunchedAndConnected";
|
|
break;
|
|
case Notification::kDisconnected:
|
|
os << "Disconnected";
|
|
break;
|
|
case Notification::kCrashed:
|
|
os << "Crashed";
|
|
break;
|
|
case Notification::kKilled:
|
|
os << "Killed";
|
|
break;
|
|
case Notification::kLaunchFailed:
|
|
os << "LaunchFailed";
|
|
break;
|
|
case Notification::kExitedNormally:
|
|
os << "ExitedNormally";
|
|
break;
|
|
}
|
|
return os;
|
|
}
|
|
|
|
// Returns true if a child process whose ID is |child_id| is still alive.
|
|
bool IsHostAlive(int child_id) {
|
|
return BrowserChildProcessHost::FromID(child_id) != nullptr;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
// A test BrowserChildProcessObserver that transforms every call to one of the
|
|
// observer's method to a call to the notification callback.
|
|
class BrowserChildProcessNotificationObserver
|
|
: public BrowserChildProcessObserver {
|
|
public:
|
|
using OnNotificationCallback =
|
|
base::RepeatingCallback<void(Notification notification)>;
|
|
|
|
BrowserChildProcessNotificationObserver(
|
|
int child_id,
|
|
OnNotificationCallback on_notification_callback)
|
|
: child_id_(child_id),
|
|
on_notification_callback_(std::move(on_notification_callback)) {
|
|
BrowserChildProcessObserver::Add(this);
|
|
}
|
|
|
|
~BrowserChildProcessNotificationObserver() override {
|
|
BrowserChildProcessObserver::Remove(this);
|
|
}
|
|
|
|
protected:
|
|
// BrowserChildProcessObserver:
|
|
void BrowserChildProcessLaunchedAndConnected(
|
|
const ChildProcessData& data) override {
|
|
OnNotification(data, Notification::kLaunchedAndConnected);
|
|
}
|
|
void BrowserChildProcessHostDisconnected(
|
|
const ChildProcessData& data) override {
|
|
OnNotification(data, Notification::kDisconnected);
|
|
}
|
|
void BrowserChildProcessCrashed(
|
|
const ChildProcessData& data,
|
|
const ChildProcessTerminationInfo& info) override {
|
|
OnNotification(data, Notification::kCrashed);
|
|
}
|
|
void BrowserChildProcessKilled(
|
|
const ChildProcessData& data,
|
|
const ChildProcessTerminationInfo& info) override {
|
|
OnNotification(data, Notification::kKilled);
|
|
}
|
|
void BrowserChildProcessLaunchFailed(
|
|
const ChildProcessData& data,
|
|
const ChildProcessTerminationInfo& info) override {
|
|
OnNotification(data, Notification::kLaunchFailed);
|
|
}
|
|
void BrowserChildProcessExitedNormally(
|
|
const ChildProcessData& data,
|
|
const ChildProcessTerminationInfo& info) override {
|
|
OnNotification(data, Notification::kExitedNormally);
|
|
}
|
|
|
|
void OnNotification(const ChildProcessData& data, Notification notification) {
|
|
if (data.id == child_id_)
|
|
on_notification_callback_.Run(notification);
|
|
}
|
|
|
|
private:
|
|
// Every notification coming for a child with a different ID will be ignored.
|
|
int child_id_;
|
|
|
|
// The callback to invoke every time a method of the observer is called.
|
|
OnNotificationCallback on_notification_callback_;
|
|
};
|
|
|
|
// A helper class that allows the user to wait until a specific |notification|
|
|
// is sent for a child process whose ID matches |child_id|.
|
|
class WaitForNotificationObserver {
|
|
public:
|
|
WaitForNotificationObserver(int child_id, Notification notification)
|
|
: inner_observer_(
|
|
child_id,
|
|
base::BindRepeating(&WaitForNotificationObserver::OnNotification,
|
|
base::Unretained(this))),
|
|
notification_(notification) {}
|
|
|
|
~WaitForNotificationObserver() = default;
|
|
|
|
// Waits until the notification is received. Returns immediately if it was
|
|
// already received.
|
|
void Wait() {
|
|
if (notification_received_)
|
|
return;
|
|
|
|
DCHECK(!run_loop_.running());
|
|
run_loop_.Run();
|
|
}
|
|
|
|
private:
|
|
void OnNotification(Notification notification) {
|
|
if (notification != notification_)
|
|
return;
|
|
|
|
notification_received_ = true;
|
|
if (run_loop_.running())
|
|
run_loop_.Quit();
|
|
}
|
|
|
|
BrowserChildProcessNotificationObserver inner_observer_;
|
|
Notification notification_;
|
|
base::RunLoop run_loop_;
|
|
bool notification_received_ = false;
|
|
};
|
|
|
|
class TestSandboxedProcessLauncherDelegate
|
|
: public SandboxedProcessLauncherDelegate {
|
|
public:
|
|
explicit TestSandboxedProcessLauncherDelegate(
|
|
sandbox::mojom::Sandbox sandbox_type)
|
|
: sandbox_type_(sandbox_type) {}
|
|
~TestSandboxedProcessLauncherDelegate() override = default;
|
|
|
|
// SandboxedProcessLauncherDelegate:
|
|
sandbox::mojom::Sandbox GetSandboxType() override { return sandbox_type_; }
|
|
|
|
private:
|
|
sandbox::mojom::Sandbox sandbox_type_;
|
|
};
|
|
|
|
// A test-specific type of process host. Self-owned.
|
|
class TestProcessHost : public BrowserChildProcessHostDelegate {
|
|
public:
|
|
static base::WeakPtr<TestProcessHost> Create() {
|
|
auto* instance = new TestProcessHost();
|
|
return instance->GetWeakPtr();
|
|
}
|
|
|
|
TestProcessHost()
|
|
: process_(BrowserChildProcessHost::Create(
|
|
PROCESS_TYPE_UTILITY,
|
|
this,
|
|
ChildProcessHost::IpcMode::kNormal)) {}
|
|
~TestProcessHost() override = default;
|
|
|
|
// Returns the ID of the child process.
|
|
int GetId() { return process_->GetData().id; }
|
|
|
|
// Binds to the test service on the child process and returns the bound
|
|
// remote.
|
|
mojo::Remote<mojom::TestService> BindTestService() {
|
|
mojo::Remote<mojom::TestService> test_service;
|
|
|
|
static_cast<ChildProcessHostImpl*>(process_->GetHost())
|
|
->child_process()
|
|
->BindServiceInterface(test_service.BindNewPipeAndPassReceiver());
|
|
|
|
return test_service;
|
|
}
|
|
|
|
// Returns the command line used to launch the child process.
|
|
std::unique_ptr<base::CommandLine> GetChildCommandLine() {
|
|
base::FilePath child_path =
|
|
ChildProcessHost::GetChildPath(ChildProcessHost::CHILD_NORMAL);
|
|
auto command_line = std::make_unique<base::CommandLine>(child_path);
|
|
|
|
command_line->AppendSwitchASCII(switches::kProcessType,
|
|
switches::kUtilityProcess);
|
|
command_line->AppendSwitchASCII(switches::kUtilitySubType,
|
|
"Test Utility Process");
|
|
sandbox::policy::SetCommandLineFlagsForSandboxType(command_line.get(),
|
|
sandbox_type_);
|
|
|
|
return command_line;
|
|
}
|
|
|
|
// Launches the child process using the default test launcher delegate.
|
|
void LaunchProcess() {
|
|
LaunchProcessWithDelegate(
|
|
std::make_unique<TestSandboxedProcessLauncherDelegate>(sandbox_type_));
|
|
}
|
|
|
|
// Launches the child process using a supplied sandbox delegate.
|
|
void LaunchProcessWithDelegate(
|
|
std::unique_ptr<SandboxedProcessLauncherDelegate>
|
|
sandboxed_process_launcher_delegate) {
|
|
process_->SetName(u"Test utility process");
|
|
|
|
auto command_line = GetChildCommandLine();
|
|
bool terminate_on_shutdown = true;
|
|
|
|
process_->Launch(std::move(sandboxed_process_launcher_delegate),
|
|
std::move(command_line), terminate_on_shutdown);
|
|
|
|
test_service_ = BindTestService();
|
|
}
|
|
|
|
// Requests the child process to shutdown.
|
|
void ForceShutdown() { process_->GetHost()->ForceShutdown(); }
|
|
|
|
// Disconnects the bound remote from the test service.
|
|
void Disconnect() { test_service_.reset(); }
|
|
|
|
// Sets the sandbox type to use for the child process.
|
|
void SetSandboxType(sandbox::mojom::Sandbox sandbox_type) {
|
|
sandbox_type_ = sandbox_type;
|
|
}
|
|
|
|
mojom::TestService* service() const { return test_service_.get(); }
|
|
|
|
base::WeakPtr<TestProcessHost> GetWeakPtr() {
|
|
return weak_ptr_factory_.GetWeakPtr();
|
|
}
|
|
|
|
private:
|
|
sandbox::mojom::Sandbox sandbox_type_ = sandbox::mojom::Sandbox::kUtility;
|
|
|
|
std::unique_ptr<BrowserChildProcessHost> process_;
|
|
|
|
mojo::Remote<mojom::TestService> test_service_;
|
|
|
|
base::WeakPtrFactory<TestProcessHost> weak_ptr_factory_{this};
|
|
};
|
|
|
|
// A helper class that exposes which notifications were sent for a specific
|
|
// child process.
|
|
class TestBrowserChildProcessObserver {
|
|
public:
|
|
explicit TestBrowserChildProcessObserver(int child_id)
|
|
: inner_observer_(child_id,
|
|
base::BindRepeating(
|
|
&TestBrowserChildProcessObserver::OnNotification,
|
|
base::Unretained(this))) {}
|
|
|
|
~TestBrowserChildProcessObserver() = default;
|
|
|
|
// Returns the notifications received for |child_id|.
|
|
const std::vector<Notification>& notifications() const {
|
|
return notifications_;
|
|
}
|
|
|
|
private:
|
|
void OnNotification(Notification notification) {
|
|
notifications_.push_back(notification);
|
|
}
|
|
|
|
BrowserChildProcessNotificationObserver inner_observer_;
|
|
|
|
std::vector<Notification> notifications_;
|
|
};
|
|
|
|
class BrowserChildProcessObserverBrowserTest : public ContentBrowserTest {};
|
|
|
|
// Tests that launching and then using ForceShutdown() results in a normal
|
|
// termination.
|
|
#if defined(ADDRESS_SANITIZER)
|
|
// TODO(crbug.com/40238612): Fix ASAN failures on trybot.
|
|
#define MAYBE_LaunchAndForceShutdown DISABLED_LaunchAndForceShutdown
|
|
#else
|
|
#define MAYBE_LaunchAndForceShutdown LaunchAndForceShutdown
|
|
#endif
|
|
IN_PROC_BROWSER_TEST_F(BrowserChildProcessObserverBrowserTest,
|
|
MAYBE_LaunchAndForceShutdown) {
|
|
base::WeakPtr<TestProcessHost> host = TestProcessHost::Create();
|
|
int child_id = host->GetId();
|
|
|
|
TestBrowserChildProcessObserver observer(child_id);
|
|
|
|
{
|
|
WaitForNotificationObserver waiter(child_id,
|
|
Notification::kLaunchedAndConnected);
|
|
host->LaunchProcess();
|
|
waiter.Wait();
|
|
}
|
|
|
|
{
|
|
WaitForNotificationObserver waiter(child_id, Notification::kDisconnected);
|
|
host->ForceShutdown();
|
|
waiter.Wait();
|
|
}
|
|
|
|
Notification kExitNotification =
|
|
#if BUILDFLAG(IS_ANDROID)
|
|
// TODO(pmonette): On Android, this currently causes a killed
|
|
// notification. Consider fixing.
|
|
Notification::kKilled;
|
|
#else
|
|
Notification::kExitedNormally;
|
|
#endif // BUILDFLAG(IS_ANDROID)
|
|
|
|
// The host should be deleted now.
|
|
EXPECT_FALSE(host);
|
|
EXPECT_FALSE(IsHostAlive(child_id));
|
|
EXPECT_THAT(observer.notifications(),
|
|
testing::ElementsAreArray({Notification::kLaunchedAndConnected,
|
|
kExitNotification,
|
|
Notification::kDisconnected}));
|
|
}
|
|
|
|
// Tests that launching and then deleting the host results in a normal
|
|
// termination.
|
|
IN_PROC_BROWSER_TEST_F(BrowserChildProcessObserverBrowserTest,
|
|
LaunchAndDelete) {
|
|
base::WeakPtr<TestProcessHost> host = TestProcessHost::Create();
|
|
int child_id = host->GetId();
|
|
|
|
TestBrowserChildProcessObserver observer(child_id);
|
|
|
|
{
|
|
WaitForNotificationObserver waiter(child_id,
|
|
Notification::kLaunchedAndConnected);
|
|
host->LaunchProcess();
|
|
waiter.Wait();
|
|
}
|
|
|
|
{
|
|
WaitForNotificationObserver waiter(child_id, Notification::kDisconnected);
|
|
delete host.get();
|
|
waiter.Wait();
|
|
}
|
|
|
|
// The host should be deleted now.
|
|
EXPECT_FALSE(host);
|
|
EXPECT_FALSE(IsHostAlive(child_id));
|
|
EXPECT_THAT(observer.notifications(),
|
|
testing::ElementsAreArray({Notification::kLaunchedAndConnected,
|
|
Notification::kExitedNormally,
|
|
Notification::kDisconnected}));
|
|
}
|
|
|
|
// Tests that launching and then disconnecting the service channel results in a
|
|
// normal termination.
|
|
// Note: This only works for services bound using BindServiceInterface(), not
|
|
// BindReceiver().
|
|
#if defined(ADDRESS_SANITIZER)
|
|
// TODO(crbug.com/40238612): Fix ASAN failures on trybot.
|
|
#define MAYBE_LaunchAndDisconnect DISABLED_LaunchAndDisconnect
|
|
#else
|
|
#define MAYBE_LaunchAndDisconnect LaunchAndDisconnect
|
|
#endif
|
|
IN_PROC_BROWSER_TEST_F(BrowserChildProcessObserverBrowserTest,
|
|
MAYBE_LaunchAndDisconnect) {
|
|
base::WeakPtr<TestProcessHost> host = TestProcessHost::Create();
|
|
int child_id = host->GetId();
|
|
|
|
TestBrowserChildProcessObserver observer(child_id);
|
|
|
|
{
|
|
WaitForNotificationObserver waiter(child_id,
|
|
Notification::kLaunchedAndConnected);
|
|
host->LaunchProcess();
|
|
waiter.Wait();
|
|
}
|
|
|
|
{
|
|
WaitForNotificationObserver waiter(child_id, Notification::kDisconnected);
|
|
host->Disconnect();
|
|
waiter.Wait();
|
|
}
|
|
|
|
Notification kExitNotification =
|
|
#if BUILDFLAG(IS_ANDROID)
|
|
// On Android, kKilled is always sent in the case of a crash.
|
|
Notification::kKilled;
|
|
#else
|
|
Notification::kExitedNormally;
|
|
#endif // BUILDFLAG(IS_ANDROID)
|
|
|
|
// The host should be deleted now.
|
|
EXPECT_FALSE(host);
|
|
EXPECT_FALSE(IsHostAlive(child_id));
|
|
EXPECT_THAT(observer.notifications(), testing::ElementsAreArray({
|
|
Notification::kLaunchedAndConnected,
|
|
kExitNotification,
|
|
Notification::kDisconnected,
|
|
}));
|
|
}
|
|
|
|
// Tests that launching and then causing a crash the host results in a crashed
|
|
// notification.
|
|
// TODO(crbug.com/40868150): Times out on Android tests.
|
|
#if BUILDFLAG(IS_ANDROID)
|
|
#define MAYBE_LaunchAndCrash DISABLED_LaunchAndCrash
|
|
#else
|
|
#define MAYBE_LaunchAndCrash LaunchAndCrash
|
|
#endif
|
|
IN_PROC_BROWSER_TEST_F(BrowserChildProcessObserverBrowserTest,
|
|
MAYBE_LaunchAndCrash) {
|
|
base::WeakPtr<TestProcessHost> host = TestProcessHost::Create();
|
|
int child_id = host->GetId();
|
|
|
|
TestBrowserChildProcessObserver observer(child_id);
|
|
|
|
{
|
|
WaitForNotificationObserver waiter(child_id,
|
|
Notification::kLaunchedAndConnected);
|
|
host->LaunchProcess();
|
|
waiter.Wait();
|
|
}
|
|
|
|
{
|
|
WaitForNotificationObserver waiter(child_id, Notification::kDisconnected);
|
|
host->service()->DoCrashImmediately(base::DoNothing());
|
|
waiter.Wait();
|
|
}
|
|
|
|
Notification kCrashedNotification =
|
|
#if BUILDFLAG(IS_ANDROID)
|
|
// On Android, kKilled is always sent in the case of a crash.
|
|
Notification::kKilled;
|
|
#else
|
|
Notification::kCrashed;
|
|
#endif // BUILDFLAG(IS_ANDROID)
|
|
|
|
// The host should be deleted now.
|
|
EXPECT_FALSE(host);
|
|
EXPECT_FALSE(IsHostAlive(child_id));
|
|
EXPECT_THAT(observer.notifications(),
|
|
testing::ElementsAreArray({Notification::kLaunchedAndConnected,
|
|
kCrashedNotification,
|
|
Notification::kDisconnected}));
|
|
}
|
|
|
|
// Tests that kLaunchFailed is correctly sent when the child process fails to
|
|
// launch.
|
|
//
|
|
// This test won't work as-is on POSIX platforms, where fork()+exec() is used to
|
|
// launch child processes, failure does not happen until exec(), therefore the
|
|
// test will see a valid child process followed by a
|
|
// TERMINATION_STATUS_ABNORMAL_TERMINATION of the forked process. However,
|
|
// posix_spawn() is used on macOS.
|
|
// See also ServiceProcessLauncherTest.FailToLaunchProcess.
|
|
#if !BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_MAC)
|
|
IN_PROC_BROWSER_TEST_F(BrowserChildProcessObserverBrowserTest, LaunchFailed) {
|
|
base::WeakPtr<TestProcessHost> host = TestProcessHost::Create();
|
|
int child_id = host->GetId();
|
|
|
|
#if BUILDFLAG(IS_WIN)
|
|
// The Windows sandbox does not like the child process being a different
|
|
// process, so launch unsandboxed for the purpose of this test.
|
|
host->SetSandboxType(sandbox::mojom::Sandbox::kNoSandbox);
|
|
#endif
|
|
|
|
// Simulate a catastrophic launch failure for all child processes by
|
|
// making the path to the process non-existent.
|
|
base::CommandLine::ForCurrentProcess()->AppendSwitchPath(
|
|
switches::kBrowserSubprocessPath,
|
|
base::FilePath(FILE_PATH_LITERAL("non_existent_path")));
|
|
|
|
TestBrowserChildProcessObserver observer(child_id);
|
|
|
|
{
|
|
WaitForNotificationObserver waiter(child_id, Notification::kLaunchFailed);
|
|
host->LaunchProcess();
|
|
waiter.Wait();
|
|
}
|
|
|
|
// The host should be deleted now.
|
|
EXPECT_FALSE(host);
|
|
EXPECT_FALSE(IsHostAlive(child_id));
|
|
EXPECT_THAT(observer.notifications(),
|
|
testing::ElementsAreArray({Notification::kLaunchFailed}));
|
|
}
|
|
#endif // !BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_MAC)
|
|
|
|
#if BUILDFLAG(IS_WIN)
|
|
class TestPreSpawnTargetFailureSandboxedProcessLauncherDelegate
|
|
: public TestSandboxedProcessLauncherDelegate {
|
|
public:
|
|
using TestSandboxedProcessLauncherDelegate::
|
|
TestSandboxedProcessLauncherDelegate;
|
|
|
|
// SandboxedProcessLauncherDelegate:
|
|
bool PreSpawnTarget(sandbox::TargetPolicy* policy) override {
|
|
// Force a failure in PreSpawnTarget().
|
|
return false;
|
|
}
|
|
};
|
|
|
|
// Override the observer to verify the error occurred in PreSpawnTarget().
|
|
class TestPreSpawnTargetFailureBrowserChildProcessNotificationObserver
|
|
: public BrowserChildProcessNotificationObserver {
|
|
public:
|
|
using BrowserChildProcessNotificationObserver::
|
|
BrowserChildProcessNotificationObserver;
|
|
|
|
// BrowserChildProcessObserver:
|
|
void BrowserChildProcessLaunchFailed(
|
|
const ChildProcessData& data,
|
|
const ChildProcessTerminationInfo& info) override {
|
|
EXPECT_EQ(info.exit_code, sandbox::SBOX_ERROR_DELEGATE_PRE_SPAWN);
|
|
BrowserChildProcessNotificationObserver::OnNotification(
|
|
data, Notification::kLaunchFailed);
|
|
}
|
|
};
|
|
|
|
// Tests that a pre spawn failure results in a failed launch.
|
|
IN_PROC_BROWSER_TEST_F(BrowserChildProcessObserverBrowserTest,
|
|
LaunchPreSpawnFailed) {
|
|
base::WeakPtr<TestProcessHost> host = TestProcessHost::Create();
|
|
int child_id = host->GetId();
|
|
|
|
TestBrowserChildProcessObserver observer(child_id);
|
|
|
|
{
|
|
WaitForNotificationObserver waiter(child_id, Notification::kLaunchFailed);
|
|
host->LaunchProcessWithDelegate(
|
|
std::make_unique<
|
|
TestPreSpawnTargetFailureSandboxedProcessLauncherDelegate>(
|
|
sandbox::mojom::Sandbox::kUtility));
|
|
waiter.Wait();
|
|
}
|
|
|
|
// The host should be deleted now.
|
|
EXPECT_FALSE(host);
|
|
EXPECT_FALSE(IsHostAlive(child_id));
|
|
EXPECT_THAT(observer.notifications(),
|
|
testing::ElementsAreArray({Notification::kLaunchFailed}));
|
|
}
|
|
#endif // BUILDFLAG(IS_WIN)
|
|
|
|
} // namespace content
|