WebContent: Terminate immediately on client disconnect

Use Core::Process::terminate_immediately() when the UI process goes
away or a sync IPC request fails because the client disconnected. This
avoids running exit-time destructors during WebContent shutdown.

Enable -Wexit-time-destructors for the WebContent executable and
service target, then convert the existing warning sites to permanent
process lifetime storage.
This commit is contained in:
Andreas Kling 2026-06-04 10:40:53 +02:00 committed by Andreas Kling
parent 164ed80244
commit 26de1ca600
6 changed files with 41 additions and 21 deletions

View file

@ -34,10 +34,12 @@ target_include_directories(webcontentservice PUBLIC $<BUILD_INTERFACE:${LADYBIRD
target_link_libraries(webcontentservice PUBLIC LibCore LibCrypto LibFileSystem LibGfx LibHTTP LibIPC LibJS LibMain LibMedia LibWasm LibWeb LibWebSocket LibRequests LibWebView LibImageDecoderClient LibGC)
target_link_libraries(webcontentservice PRIVATE OpenSSL::Crypto OpenSSL::SSL)
target_link_libraries(webcontentservice PRIVATE SDL3::SDL3)
target_compile_options(webcontentservice PRIVATE $<$<COMPILE_LANG_AND_ID:CXX,Clang,AppleClang>:-Wexit-time-destructors>)
add_executable(WebContent main.cpp)
target_link_libraries(WebContent PRIVATE webcontentservice LibURL)
target_compile_options(WebContent PRIVATE $<$<COMPILE_LANG_AND_ID:CXX,Clang,AppleClang>:-Wexit-time-destructors>)
if(WIN32)
ladybird_windows_bin(WebContent CONSOLE)

View file

@ -14,6 +14,7 @@
#include <AK/JsonObject.h>
#include <AK/OwnPtr.h>
#include <AK/QuickSort.h>
#include <LibCore/Process.h>
#include <LibCore/System.h>
#include <LibGC/Heap.h>
#include <LibGfx/Bitmap.h>
@ -100,7 +101,7 @@ void ConnectionFromClient::did_destroy_compositor_context(Web::Compositor::Compo
void ConnectionFromClient::die()
{
_exit(0);
Core::Process::terminate_immediately(0);
}
Messages::WebContentServer::InitTransportResponse ConnectionFromClient::init_transport([[maybe_unused]] int peer_pid)

View file

@ -10,6 +10,7 @@
#include <AK/JsonObjectSerializer.h>
#include <AK/JsonValue.h>
#include <AK/Math.h>
#include <LibCore/Process.h>
#include <LibCore/Timer.h>
#include <LibGfx/Bitmap.h>
#include <LibGfx/ShareableBitmap.h>
@ -605,7 +606,7 @@ HTTP::Cookie::VersionedCookie PageClient::page_did_request_cookie(URL::URL const
auto response = client().send_sync_but_allow_failure<Messages::WebContentClient::DidRequestCookie>(m_id, url, source);
if (!response) {
dbgln("WebContent client disconnected during DidRequestCookie. Exiting peacefully.");
exit(0);
Core::Process::terminate_immediately(0);
}
return response->take_cookie();
}
@ -615,7 +616,7 @@ void PageClient::page_did_set_cookie(URL::URL const& url, HTTP::Cookie::ParsedCo
auto response = client().send_sync_but_allow_failure<Messages::WebContentClient::DidSetCookie>(url, cookie, source);
if (!response) {
dbgln("WebContent client disconnected during DidSetCookie. Exiting peacefully.");
exit(0);
Core::Process::terminate_immediately(0);
}
}
@ -647,7 +648,7 @@ bool PageClient::page_did_is_known_hsts_host(String const& domain)
auto response = client().send_sync_but_allow_failure<Messages::WebContentClient::DidIsKnownHstsHost>(domain);
if (!response) {
dbgln("WebContent client disconnected during DidIsKnownHstsHost. Exiting peacefully.");
exit(0);
Core::Process::terminate_immediately(0);
}
return response->result();
}
@ -657,7 +658,7 @@ Optional<String> PageClient::page_did_request_storage_item(Web::StorageAPI::Stor
auto response = client().send_sync_but_allow_failure<Messages::WebContentClient::DidRequestStorageItem>(storage_endpoint, storage_key, bottle_key);
if (!response) {
dbgln("WebContent client disconnected during DidRequestStorageItem. Exiting peacefully.");
exit(0);
Core::Process::terminate_immediately(0);
}
return response->take_value();
}
@ -667,7 +668,7 @@ WebView::StorageSetResult PageClient::page_did_set_storage_item(Web::StorageAPI:
auto response = client().send_sync_but_allow_failure<Messages::WebContentClient::DidSetStorageItem>(storage_endpoint, storage_key, bottle_key, value);
if (!response) {
dbgln("WebContent client disconnected during DidSetStorageItem. Exiting peacefully.");
exit(0);
Core::Process::terminate_immediately(0);
}
return response->result();
}
@ -677,7 +678,7 @@ void PageClient::page_did_remove_storage_item(Web::StorageAPI::StorageEndpointTy
auto response = client().send_sync_but_allow_failure<Messages::WebContentClient::DidRemoveStorageItem>(storage_endpoint, storage_key, bottle_key);
if (!response) {
dbgln("WebContent client disconnected during DidRemoveStorageItem. Exiting peacefully.");
exit(0);
Core::Process::terminate_immediately(0);
}
}
@ -686,7 +687,7 @@ Vector<String> PageClient::page_did_request_storage_keys(Web::StorageAPI::Storag
auto response = client().send_sync_but_allow_failure<Messages::WebContentClient::DidRequestStorageKeys>(storage_endpoint, storage_key);
if (!response) {
dbgln("WebContent client disconnected during DidRequestStorageKeys. Exiting peacefully.");
exit(0);
Core::Process::terminate_immediately(0);
}
return response->take_keys();
}
@ -696,7 +697,7 @@ void PageClient::page_did_clear_storage(Web::StorageAPI::StorageEndpointType sto
auto response = client().send_sync_but_allow_failure<Messages::WebContentClient::DidClearStorage>(storage_endpoint, storage_key);
if (!response) {
dbgln("WebContent client disconnected during DidClearStorage. Exiting peacefully.");
exit(0);
Core::Process::terminate_immediately(0);
}
}
@ -720,7 +721,7 @@ PageClient::NewWebViewResult PageClient::page_did_request_new_web_view(Web::HTML
auto response = client().send_sync_but_allow_failure<Messages::WebContentClient::DidRequestNewWebView>(m_id, activate_tab, hints);
if (!response) {
dbgln("WebContent client disconnected during DidRequestNewWebView. Exiting peacefully.");
exit(0);
Core::Process::terminate_immediately(0);
}
auto& new_client = m_owner.create_page(response->new_page_id());
@ -819,7 +820,7 @@ Web::HTML::WorkerAgentId PageClient::start_worker_agent(Web::HTML::WorkerAgentSt
auto response = client().send_sync_but_allow_failure<Messages::WebContentClient::StartWorkerAgent>(m_id, move(request));
if (!response) {
dbgln("WebContent client disconnected during StartWorkerAgent. Exiting peacefully.");
exit(0);
Core::Process::terminate_immediately(0);
}
return response->agent_id();

View file

@ -11,6 +11,7 @@
#include <AK/JsonObject.h>
#include <AK/JsonValue.h>
#include <AK/LexicalPath.h>
#include <AK/NeverDestroyed.h>
#include <AK/Time.h>
#include <AK/Vector.h>
#include <LibCore/File.h>
@ -2667,11 +2668,11 @@ ErrorOr<void, Web::WebDriver::Error> WebDriverConnection::ensure_current_top_lev
// https://w3c.github.io/webdriver/#dfn-get-the-prompt-handler
Web::WebDriver::PromptHandlerConfiguration WebDriverConnection::get_the_prompt_handler(Web::WebDriver::PromptType type) const
{
static Web::WebDriver::UserPromptHandler::ValueType empty_user_prompt_handler;
static NeverDestroyed<Web::WebDriver::UserPromptHandler::ValueType> empty_user_prompt_handler;
auto const& user_prompt_handler = Web::WebDriver::user_prompt_handler();
// 1. If the user prompt handler is null, let handlers be an empty map. Otherwise let handlers be user prompt handler.
auto const& handlers = user_prompt_handler.has_value() ? *user_prompt_handler : empty_user_prompt_handler;
auto const& handlers = user_prompt_handler.has_value() ? *user_prompt_handler : *empty_user_prompt_handler;
// 2. If handlers contains type return handlers[type].
if (auto handler = handlers.get(type); handler.has_value())

View file

@ -6,6 +6,7 @@
#include <AK/FlyString.h>
#include <AK/JsonObject.h>
#include <AK/NeverDestroyed.h>
#include <LibWeb/DOM/CustomEvent.h>
#include <LibWeb/DOM/Document.h>
#include <LibWeb/DOM/Event.h>
@ -17,9 +18,23 @@
namespace WebContent {
static auto LADYBIRD_PROPERTY = JS::PropertyKey { "ladybird"_utf16_fly_string };
static auto WEB_UI_LOADED_EVENT = "WebUILoaded"_fly_string;
static auto WEB_UI_MESSAGE_EVENT = "WebUIMessage"_fly_string;
static JS::PropertyKey const& ladybird_property()
{
static NeverDestroyed<JS::PropertyKey> property { "ladybird"_utf16_fly_string };
return *property;
}
static FlyString const& web_ui_loaded_event()
{
static NeverDestroyed<FlyString> event { "WebUILoaded"_fly_string };
return *event;
}
static FlyString const& web_ui_message_event()
{
static NeverDestroyed<FlyString> event { "WebUIMessage"_fly_string };
return *event;
}
ErrorOr<NonnullRefPtr<WebUIConnection>> WebUIConnection::connect(IPC::TransportHandle handle, Web::DOM::Document& document)
{
@ -32,10 +47,10 @@ WebUIConnection::WebUIConnection(NonnullOwnPtr<IPC::Transport> transport, Web::D
, m_document(document)
{
auto& realm = m_document->realm();
m_document->window()->define_direct_property(LADYBIRD_PROPERTY, realm.create<Web::Internals::WebUI>(realm), JS::default_attributes);
m_document->window()->define_direct_property(ladybird_property(), realm.create<Web::Internals::WebUI>(realm), JS::default_attributes);
Web::HTML::queue_a_task(Web::HTML::Task::Source::Unspecified, nullptr, m_document, GC::create_function(realm.heap(), [&document = *m_document]() {
document.dispatch_event(Web::DOM::Event::create(document.realm(), WEB_UI_LOADED_EVENT));
document.dispatch_event(Web::DOM::Event::create(document.realm(), web_ui_loaded_event()));
}));
}
@ -44,7 +59,7 @@ WebUIConnection::~WebUIConnection()
if (!m_document->window())
return;
(void)m_document->window()->internal_delete(LADYBIRD_PROPERTY);
(void)m_document->window()->internal_delete(ladybird_property());
}
void WebUIConnection::visit_edges(JS::Cell::Visitor& visitor)
@ -73,7 +88,7 @@ void WebUIConnection::send_message(String name, JsonValue data)
Web::Bindings::CustomEventInit event_init {};
event_init.detail = serialized_detail.value();
m_document->dispatch_event(Web::DOM::CustomEvent::create(realm, WEB_UI_MESSAGE_EVENT, event_init));
m_document->dispatch_event(Web::DOM::CustomEvent::create(realm, web_ui_message_event(), event_init));
}
void WebUIConnection::received_message_from_web_ui(String const& name, JS::Value data)

View file

@ -84,7 +84,7 @@ static void crash_signal_handler(int signo)
}
warnln("\n\033[31;1mCRASH\033[0m: Received signal {} ({})", name, signo);
dump_backtrace(2, 100);
exit(128 + signo);
Core::Process::terminate_immediately(128 + signo);
}
static void install_crash_signal_handlers()