WebContent: Exit cleanly when browser IPC disconnects

Exit WebContent immediately when either browser-side IPC peer
disconnects. Plumb Unix process exit status through LibWebView so the
browser process can tell clean owner-driven shutdown apart from renderer
crashes.

This keeps nonzero exits and signal deaths reported as crashes, while
letting status 0 exits disappear without making test-web report the page
as crashed.
This commit is contained in:
Andreas Kling 2026-05-12 17:20:40 +02:00 committed by Andreas Kling
parent 9a0bcd930b
commit a031d00695
9 changed files with 29 additions and 18 deletions

View file

@ -35,6 +35,10 @@
# include <LibIPC/TransportBootstrapMach.h>
#endif
#if !defined(AK_OS_WINDOWS)
# include <sys/wait.h>
#endif
namespace WebView {
Application* Application::s_the = nullptr;
@ -472,8 +476,8 @@ ErrorOr<void> Application::launch_services()
m_bookmark_store_observer = make<ApplicationBookmarkStoreObserver>();
m_process_manager = make<ProcessManager>();
m_process_manager->on_process_exited = [this](Process&& process) {
process_did_exit(move(process));
m_process_manager->on_process_exited = [this](Process&& process, Optional<int> exit_status) {
process_did_exit(move(process), exit_status);
};
if (m_browser_options.disable_sql_database == DisableSQLDatabase::No) {
@ -727,7 +731,7 @@ Optional<Process&> Application::find_process(pid_t pid)
return m_process_manager->find_process(pid);
}
void Application::process_did_exit(Process&& process)
void Application::process_did_exit(Process&& process, Optional<int> exit_status)
{
if (m_event_loop->was_exit_requested())
return;
@ -751,6 +755,10 @@ void Application::process_did_exit(Process&& process)
}
break;
case ProcessType::WebContent:
#if !defined(AK_OS_WINDOWS)
if (exit_status.has_value() && WIFEXITED(*exit_status) && WEXITSTATUS(*exit_status) == 0)
break;
#endif
if (auto client = process.client<WebContentClient>(); client.has_value())
client->notify_all_views_of_crash();
break;

View file

@ -171,7 +171,7 @@ protected:
ErrorOr<void> initialize(Main::Arguments const&);
virtual void process_did_exit(Process&&);
virtual void process_did_exit(Process&&, Optional<int> exit_status);
virtual void create_platform_arguments(Core::ArgsParser&) { }
virtual void create_platform_options(BrowserOptions&, RequestServerOptions&, WebContentOptions&) { }

View file

@ -49,10 +49,10 @@ StringView process_name_from_type(ProcessType type)
ProcessManager::ProcessManager()
: on_process_added([](Process&) {})
, on_process_exited([](Process&&) {})
, m_process_monitor(ProcessMonitor([this](pid_t pid) {
, on_process_exited([](Process&&, Optional<int>) {})
, m_process_monitor(ProcessMonitor([this](pid_t pid, Optional<int> exit_status) {
if (auto process = remove_process(pid); process.has_value())
on_process_exited(process.release_value());
on_process_exited(process.release_value(), exit_status);
}))
{
add_process(Process(WebView::ProcessType::Browser, nullptr, Core::Process::current()));

View file

@ -40,7 +40,7 @@ public:
JsonValue serialize_json();
Function<void(Process&)> on_process_added; // test-web
Function<void(Process&&)> on_process_exited;
Function<void(Process&&, Optional<int> exit_status)> on_process_exited;
private:
void verify_event_loop() const;

View file

@ -12,7 +12,7 @@
namespace WebView {
ProcessMonitor::ProcessMonitor(Function<void(pid_t)> exit_handler)
ProcessMonitor::ProcessMonitor(Function<void(pid_t, Optional<int> exit_status)> exit_handler)
: m_on_process_exit(move(exit_handler))
{
#if !defined(AK_OS_WINDOWS)
@ -23,7 +23,7 @@ ProcessMonitor::ProcessMonitor(Function<void(pid_t)> exit_handler)
if (m_monitored_processes.contains(pid)) {
if (WIFEXITED(status) || WIFSIGNALED(status)) {
m_monitored_processes.remove(pid);
m_on_process_exit(pid);
m_on_process_exit(pid, status);
}
}
result = Core::System::waitpid(-1, WNOHANG);
@ -51,7 +51,7 @@ void ProcessMonitor::add_process(pid_t pid)
#if defined(AK_OS_WINDOWS)
Core::EventLoop::register_process(pid, [this](pid_t pid) {
m_monitored_processes.remove(pid);
m_on_process_exit(pid);
m_on_process_exit(pid, {});
});
#endif
}

View file

@ -13,13 +13,13 @@ namespace WebView {
class ProcessMonitor {
public:
ProcessMonitor(Function<void(pid_t)> exit_handler);
ProcessMonitor(Function<void(pid_t, Optional<int> exit_status)> exit_handler);
~ProcessMonitor();
void add_process(pid_t pid);
private:
Function<void(pid_t)> m_on_process_exit;
Function<void(pid_t, Optional<int> exit_status)> m_on_process_exit;
HashTable<pid_t> m_monitored_processes;
[[maybe_unused]] int m_signal_handle { -1 };
};

View file

@ -78,7 +78,10 @@ class CompositorConnectionFromClient final
C_OBJECT(CompositorConnectionFromClient)
public:
virtual void die() override { }
virtual void die() override
{
_exit(0);
}
private:
explicit CompositorConnectionFromClient(NonnullOwnPtr<IPC::Transport> transport)
@ -134,7 +137,7 @@ ConnectionFromClient::~ConnectionFromClient() = default;
void ConnectionFromClient::die()
{
Web::Platform::EventLoopPlugin::the().quit();
_exit(0);
}
Messages::WebContentServer::InitTransportResponse ConnectionFromClient::init_transport([[maybe_unused]] int peer_pid)

View file

@ -29,9 +29,9 @@ TestRunCapture::TestRunCapture()
setup_output_capture_for_helper_process(process);
};
m_previous_on_process_exited = move(process_manager.on_process_exited);
process_manager.on_process_exited = [this](WebView::Process&& process) {
process_manager.on_process_exited = [this](WebView::Process&& process, Optional<int> exit_status) {
consume_helper_capture(process.pid());
m_previous_on_process_exited(move(process));
m_previous_on_process_exited(move(process), exit_status);
};
process_manager.for_each_process([this](WebView::Process& process) {

View file

@ -72,7 +72,7 @@ private:
void consume_helper_capture(pid_t pid);
void destroy_view_capture_of(TestWebView const& view);
Function<void(WebView::Process&&)> m_previous_on_process_exited;
Function<void(WebView::Process&&, Optional<int> exit_status)> m_previous_on_process_exited;
HashMap<TestWebView const*, NonnullOwnPtr<ViewOutputCapture>> m_test_output_captures;
HashMap<pid_t, NonnullOwnPtr<HelperOutputCapture>> m_helper_output_captures;
CaptureFile m_helper_output;