LibWeb+LibWebView: Move WebWorkerClient into LibWebView

This mirrors WebContentClient and allows Workers to directly interact
with WebView APIs such as the CookieJar.
This commit is contained in:
Luke Wilde 2026-05-21 18:11:05 +02:00 committed by Alexander Kalenik
parent c2dbd52982
commit 7e6d4b7b7b
12 changed files with 117 additions and 180 deletions

View file

@ -1185,7 +1185,6 @@ set(SOURCES
WebXR/XRSessionEvent.cpp
WebXR/XRSystem.cpp
WebXR/XRWebGLLayer.cpp
Worker/WebWorkerClient.cpp
XHR/EventNames.cpp
XHR/FormData.cpp
XHR/FormDataIterator.cpp

View file

@ -850,7 +850,6 @@ class UserActivation;
class ValidityState;
class VideoTrack;
class VideoTrackList;
class WebWorkerClient;
class Window;
class WindowEnvironmentSettingsObject;
class WindowProxy;

View file

@ -24,7 +24,6 @@
#include <LibWeb/HTML/WorkerGlobalScope.h>
#include <LibWeb/Page/Page.h>
#include <LibWeb/StorageAPI/StorageKey.h>
#include <LibWeb/Worker/WebWorkerClient.h>
namespace Web::HTML {
@ -153,15 +152,8 @@ WebIDL::ExceptionOr<void> BroadcastChannel::post_message(JS::Value message)
// Steps 6-9.
deliver_message_locally(message_to_send);
// NB: Other WebContent processes receive this via the browser-process IPC fanout.
// Child worker processes are not part of that routing path, so forward to them directly here.
Bindings::principal_host_defined_page(realm()).client().page_did_post_broadcast_channel_message(message_to_send);
WebWorkerClient::for_each_client([&](WebWorkerClient& client) {
client.async_broadcast_channel_message(message_to_send);
return IterationDecision::Continue;
});
return {};
}

View file

@ -1,86 +0,0 @@
/*
* Copyright (c) 2023, Andrew Kaster <akaster@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibWeb/Worker/WebWorkerClient.h>
namespace Web::HTML {
HashTable<WebWorkerClient*> WebWorkerClient::s_all_clients;
void WebWorkerClient::die()
{
if (on_worker_died)
on_worker_died();
}
void WebWorkerClient::did_close_worker()
{
if (on_worker_close)
on_worker_close();
}
void WebWorkerClient::did_finish_loading_worker_script(bool worker_is_secure_context)
{
if (on_worker_script_load_success)
on_worker_script_load_success(worker_is_secure_context);
}
void WebWorkerClient::did_fail_loading_worker_script()
{
if (on_worker_script_load_failure)
on_worker_script_load_failure();
}
void WebWorkerClient::did_report_worker_exception(String message, String filename, u32 lineno, u32 colno)
{
if (on_worker_exception)
on_worker_exception(move(message), move(filename), lineno, colno);
}
Messages::WebWorkerClient::DidRequestCookieResponse WebWorkerClient::did_request_cookie(URL::URL url, HTTP::Cookie::Source source)
{
if (on_request_cookie)
return on_request_cookie(url, source);
return HTTP::Cookie::VersionedCookie {};
}
void WebWorkerClient::did_request_file(ByteString path, i32 request_id)
{
if (on_request_file)
on_request_file(move(path), request_id);
}
void WebWorkerClient::did_post_broadcast_channel_message(Web::HTML::BroadcastChannelMessage message)
{
if (on_post_broadcast_channel_message)
on_post_broadcast_channel_message(move(message));
}
Messages::WebWorkerClient::StartWorkerAgentResponse WebWorkerClient::start_worker_agent(Web::HTML::WorkerAgentStartRequest request)
{
if (on_start_worker_agent)
return on_start_worker_agent(move(request));
return { 0 };
}
void WebWorkerClient::close_worker_agent(Web::HTML::WorkerAgentId agent_id, Web::HTML::WorkerAgentOwnerToken owner_token)
{
if (on_close_worker_agent)
on_close_worker_agent(agent_id, owner_token);
}
WebWorkerClient::WebWorkerClient(NonnullOwnPtr<IPC::Transport> transport)
: IPC::ConnectionToServer<WebWorkerClientEndpoint, WebWorkerServerEndpoint>(*this, move(transport))
{
s_all_clients.set(this);
}
WebWorkerClient::~WebWorkerClient()
{
s_all_clients.remove(this);
}
}

View file

@ -28,6 +28,7 @@ set(SOURCES
Utilities.cpp
ViewImplementation.cpp
WebContentClient.cpp
WebWorkerClient.cpp
WorkerProcessManager.cpp
WebUI.cpp
WebUI/BookmarksUI.cpp

View file

@ -25,6 +25,7 @@ class ProcessManager;
class Settings;
class ViewImplementation;
class WebContentClient;
class WebWorkerClient;
class WebUI;
struct Attribute;

View file

@ -56,7 +56,7 @@ static ErrorOr<NonnullRefPtr<ClientType>> launch_server_process(
if constexpr (requires { client->set_pid(pid_t {}); })
client->set_pid(process.pid());
if constexpr (requires { client->transport().set_peer_pid(0); } && !IsSame<ClientType, Web::HTML::WebWorkerClient>) {
if constexpr (requires { client->transport().set_peer_pid(0); } && !IsSame<ClientType, WebWorkerClient>) {
auto response = client->template send_sync<typename ClientType::InitTransport>(Core::System::getpid());
client->transport().set_peer_pid(response->peer_pid());
}
@ -185,7 +185,7 @@ ErrorOr<NonnullRefPtr<WebView::CompositorClient>> launch_compositor_process()
return launch_server_process<WebView::CompositorClient>("Compositor"sv, move(arguments));
}
ErrorOr<NonnullRefPtr<Web::HTML::WebWorkerClient>> launch_web_worker_process(Web::Bindings::AgentType type)
ErrorOr<NonnullRefPtr<WebWorkerClient>> launch_web_worker_process(Web::Bindings::AgentType type, Web::HTML::WorkerAgentId agent_id)
{
auto const& web_content_options = WebView::Application::web_content_options();
@ -218,7 +218,7 @@ ErrorOr<NonnullRefPtr<Web::HTML::WebWorkerClient>> launch_web_worker_process(Web
arguments.append(server.value());
}
return launch_server_process<Web::HTML::WebWorkerClient>("WebWorker"sv, move(arguments));
return launch_server_process<WebWorkerClient>("WebWorker"sv, move(arguments), agent_id);
}
ErrorOr<NonnullRefPtr<Requests::RequestClient>> launch_request_server_process()

View file

@ -12,10 +12,10 @@
#include <LibImageDecoderClient/Client.h>
#include <LibRequests/RequestClient.h>
#include <LibWeb/Bindings/MainThreadVM.h>
#include <LibWeb/Worker/WebWorkerClient.h>
#include <LibWebView/Forward.h>
#include <LibWebView/ViewImplementation.h>
#include <LibWebView/WebContentClient.h>
#include <LibWebView/WebWorkerClient.h>
namespace WebView {
@ -25,7 +25,7 @@ WEBVIEW_API ErrorOr<NonnullRefPtr<WebView::WebContentClient>> launch_spare_web_c
WEBVIEW_API ErrorOr<NonnullRefPtr<ImageDecoderClient::Client>> launch_image_decoder_process();
WEBVIEW_API ErrorOr<NonnullRefPtr<WebView::CompositorClient>> launch_compositor_process();
WEBVIEW_API ErrorOr<NonnullRefPtr<Web::HTML::WebWorkerClient>> launch_web_worker_process(Web::Bindings::AgentType);
WEBVIEW_API ErrorOr<NonnullRefPtr<WebView::WebWorkerClient>> launch_web_worker_process(Web::Bindings::AgentType, Web::HTML::WorkerAgentId);
WEBVIEW_API ErrorOr<NonnullRefPtr<Requests::RequestClient>> launch_request_server_process();
WEBVIEW_API ErrorOr<IPC::TransportHandle> connect_new_request_server_client();

View file

@ -0,0 +1,77 @@
/*
* Copyright (c) 2023, Andrew Kaster <akaster@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibWebView/Application.h>
#include <LibWebView/CookieJar.h>
#include <LibWebView/WebWorkerClient.h>
#include <LibWebView/WorkerProcessManager.h>
namespace WebView {
void WebWorkerClient::die()
{
WorkerProcessManager::the().worker_did_die(m_agent_id);
// Otherwise nested workers we own would outlive us, in violation of the HTML spec.
WorkerProcessManager::the().remove_web_worker_owner(*this);
}
void WebWorkerClient::did_close_worker()
{
WorkerProcessManager::the().worker_did_close(m_agent_id);
}
void WebWorkerClient::did_finish_loading_worker_script(bool worker_is_secure_context)
{
WorkerProcessManager::the().worker_did_finish_loading_script(m_agent_id, worker_is_secure_context);
}
void WebWorkerClient::did_fail_loading_worker_script()
{
WorkerProcessManager::the().worker_did_fail_loading_script(m_agent_id);
}
void WebWorkerClient::did_report_worker_exception(String message, String filename, u32 lineno, u32 colno)
{
WorkerProcessManager::the().worker_did_report_exception(m_agent_id, move(message), move(filename), lineno, colno);
}
Messages::WebWorkerClient::DidRequestCookieResponse WebWorkerClient::did_request_cookie(URL::URL url, HTTP::Cookie::Source source)
{
HTTP::Cookie::VersionedCookie cookie;
cookie.cookie = Application::cookie_jar().get_cookie(url, source);
return cookie;
}
void WebWorkerClient::did_request_file(ByteString path, i32 request_id)
{
WorkerProcessManager::the().worker_did_request_file(m_agent_id, move(path), request_id);
}
void WebWorkerClient::did_post_broadcast_channel_message(Web::HTML::BroadcastChannelMessage message)
{
WorkerProcessManager::the().worker_did_post_broadcast_channel_message(m_agent_id, move(message));
}
Messages::WebWorkerClient::StartWorkerAgentResponse WebWorkerClient::start_worker_agent(Web::HTML::WorkerAgentStartRequest request)
{
return WorkerProcessManager::the().start_worker_agent(*this, move(request));
}
void WebWorkerClient::close_worker_agent(Web::HTML::WorkerAgentId agent_id, Web::HTML::WorkerAgentOwnerToken owner_token)
{
WorkerProcessManager::the().close_worker_agent(*this, agent_id, owner_token);
}
WebWorkerClient::WebWorkerClient(NonnullOwnPtr<IPC::Transport> transport, Web::HTML::WorkerAgentId agent_id)
: IPC::ConnectionToServer<WebWorkerClientEndpoint, WebWorkerServerEndpoint>(*this, move(transport))
, m_agent_id(agent_id)
{
}
WebWorkerClient::~WebWorkerClient() = default;
}

View file

@ -7,30 +7,25 @@
#pragma once
#include <AK/ByteString.h>
#include <AK/HashTable.h>
#include <AK/IterationDecision.h>
#include <AK/Types.h>
#include <LibHTTP/Cookie/Cookie.h>
#include <LibIPC/ConnectionToServer.h>
#include <LibIPC/TransportHandle.h>
#include <LibWeb/Export.h>
#include <LibWeb/HTML/BroadcastChannelMessage.h>
#include <LibWeb/HTML/WorkerAgentTypes.h>
#include <LibWeb/Worker/WebWorkerClientEndpoint.h>
#include <LibWeb/Worker/WebWorkerServerEndpoint.h>
#include <LibWebView/Export.h>
namespace Web::HTML {
namespace WebView {
class WEB_API WebWorkerClient final
class WEBVIEW_API WebWorkerClient final
: public IPC::ConnectionToServer<WebWorkerClientEndpoint, WebWorkerServerEndpoint>
, public WebWorkerClientEndpoint {
C_OBJECT_ABSTRACT(WebWorkerClient);
public:
template<typename Callback>
static void for_each_client(Callback callback);
explicit WebWorkerClient(NonnullOwnPtr<IPC::Transport>);
explicit WebWorkerClient(NonnullOwnPtr<IPC::Transport>, Web::HTML::WorkerAgentId agent_id);
~WebWorkerClient();
pid_t pid() const { return m_pid; }
@ -46,31 +41,11 @@ public:
virtual Messages::WebWorkerClient::StartWorkerAgentResponse start_worker_agent(Web::HTML::WorkerAgentStartRequest request) override;
virtual void close_worker_agent(Web::HTML::WorkerAgentId, Web::HTML::WorkerAgentOwnerToken) override;
Function<void()> on_worker_close;
Function<void()> on_worker_died;
Function<void(bool)> on_worker_script_load_success;
Function<void()> on_worker_script_load_failure;
Function<void(String, String, u32, u32)> on_worker_exception;
Function<HTTP::Cookie::VersionedCookie(URL::URL const&, HTTP::Cookie::Source)> on_request_cookie;
Function<void(ByteString, i32)> on_request_file;
Function<void(Web::HTML::BroadcastChannelMessage)> on_post_broadcast_channel_message;
Function<Web::HTML::WorkerAgentId(Web::HTML::WorkerAgentStartRequest)> on_start_worker_agent;
Function<void(Web::HTML::WorkerAgentId, Web::HTML::WorkerAgentOwnerToken)> on_close_worker_agent;
private:
virtual void die() override;
pid_t m_pid { -1 };
static HashTable<WebWorkerClient*> s_all_clients;
Web::HTML::WorkerAgentId m_agent_id { 0 };
};
template<typename Callback>
void WebWorkerClient::for_each_client(Callback callback)
{
for (auto* client : s_all_clients) {
if (callback(*client) == IterationDecision::Break)
return;
}
}
}

View file

@ -7,11 +7,9 @@
#include <LibCore/EventLoop.h>
#include <LibCore/File.h>
#include <LibIPC/File.h>
#include <LibWeb/Worker/WebWorkerClient.h>
#include <LibWebView/Application.h>
#include <LibWebView/CookieJar.h>
#include <LibWebView/HelperProcess.h>
#include <LibWebView/WebContentClient.h>
#include <LibWebView/WebWorkerClient.h>
#include <LibWebView/WorkerProcessManager.h>
namespace WebView {
@ -34,7 +32,7 @@ Web::HTML::WorkerAgentId WorkerProcessManager::start_worker_agent(WebContentClie
return start_worker_agent(move(abstract_owner), move(request));
}
Web::HTML::WorkerAgentId WorkerProcessManager::start_worker_agent(Web::HTML::WebWorkerClient& owner, Web::HTML::WorkerAgentStartRequest request)
Web::HTML::WorkerAgentId WorkerProcessManager::start_worker_agent(WebWorkerClient& owner, Web::HTML::WorkerAgentStartRequest request)
{
auto abstract_owner = Owner {
.client = WebWorkerOwner {
@ -113,40 +111,7 @@ Web::HTML::WorkerAgentId WorkerProcessManager::start_worker_agent(Owner owner, W
// AD-HOC: For DedicatedWorker there is no shared worker manager step; we always launch a fresh
// worker process here.
auto agent_id = ++m_next_agent_id;
auto client = MUST(launch_web_worker_process(request.agent_type));
client->on_worker_close = [this, agent_id] {
worker_did_close(agent_id);
};
client->on_worker_script_load_success = [this, agent_id](bool worker_is_secure_context) {
worker_did_finish_loading_script(agent_id, worker_is_secure_context);
};
client->on_worker_script_load_failure = [this, agent_id] {
worker_did_fail_loading_script(agent_id);
};
client->on_worker_exception = [this, agent_id](String message, String filename, u32 lineno, u32 colno) {
worker_did_report_exception(agent_id, move(message), move(filename), lineno, colno);
};
client->on_worker_died = [this, agent_id] {
worker_did_die(agent_id);
};
client->on_request_cookie = [](URL::URL const& url, HTTP::Cookie::Source source) {
HTTP::Cookie::VersionedCookie cookie;
cookie.cookie = Application::cookie_jar().get_cookie(url, source);
return cookie;
};
client->on_request_file = [this, agent_id](ByteString path, i32 request_id) {
worker_did_request_file(agent_id, move(path), request_id);
};
client->on_post_broadcast_channel_message = [this, agent_id](Web::HTML::BroadcastChannelMessage message) {
worker_did_post_broadcast_channel_message(agent_id, move(message));
};
client->on_start_worker_agent = [this, client = client.ptr()](Web::HTML::WorkerAgentStartRequest request) {
return start_worker_agent(*client, move(request));
};
client->on_close_worker_agent = [this, client = client.ptr()](Web::HTML::WorkerAgentId agent_id, Web::HTML::WorkerAgentOwnerToken owner_token) {
close_worker_agent(*client, agent_id, owner_token);
};
auto client = MUST(launch_web_worker_process(request.agent_type, agent_id));
auto request_server_handle = MUST(connect_new_request_server_client());
auto image_decoder_handle = MUST(connect_new_image_decoder_client());
@ -196,7 +161,7 @@ void WorkerProcessManager::close_worker_agent(WebContentClient& client, Web::HTM
remove_owner(agent_id, identity);
}
void WorkerProcessManager::close_worker_agent(Web::HTML::WebWorkerClient& client, Web::HTML::WorkerAgentId agent_id, Web::HTML::WorkerAgentOwnerToken owner_token)
void WorkerProcessManager::close_worker_agent(WebWorkerClient& client, Web::HTML::WorkerAgentId agent_id, Web::HTML::WorkerAgentOwnerToken owner_token)
{
Owner identity {
.client = WebWorkerOwner { .client = client },
@ -222,6 +187,23 @@ void WorkerProcessManager::remove_web_content_owner(WebContentClient& client)
remove_agent(agent_id);
}
void WorkerProcessManager::remove_web_worker_owner(WebWorkerClient& client)
{
Vector<Web::HTML::WorkerAgentId> agents_to_close;
for (auto& entry : m_agents) {
auto& agent = entry.value;
agent.owners.remove_all_matching([&](Owner const& owner) {
auto const* web_worker_owner = owner.client.get_pointer<WebWorkerOwner>();
return web_worker_owner && web_worker_owner->client.ptr() == &client;
});
if (agent.owners.is_empty())
agents_to_close.append(agent.id);
}
for (auto agent_id : agents_to_close)
remove_agent(agent_id);
}
void WorkerProcessManager::broadcast_channel_message_from_web_content(Web::HTML::BroadcastChannelMessage const& message)
{
for (auto& entry : m_agents) {

View file

@ -16,12 +16,6 @@
#include <LibWeb/HTML/WorkerAgentTypes.h>
#include <LibWebView/Forward.h>
namespace Web::HTML {
class WebWorkerClient;
}
namespace WebView {
class WorkerProcessManager {
@ -37,15 +31,18 @@ public:
};
Web::HTML::WorkerAgentId start_worker_agent(WebContentClient&, u64 page_id, Web::HTML::WorkerAgentStartRequest);
Web::HTML::WorkerAgentId start_worker_agent(Web::HTML::WebWorkerClient&, Web::HTML::WorkerAgentStartRequest);
Web::HTML::WorkerAgentId start_worker_agent(WebWorkerClient&, Web::HTML::WorkerAgentStartRequest);
void close_worker_agent(WebContentClient&, Web::HTML::WorkerAgentId, Web::HTML::WorkerAgentOwnerToken);
void close_worker_agent(Web::HTML::WebWorkerClient&, Web::HTML::WorkerAgentId, Web::HTML::WorkerAgentOwnerToken);
void close_worker_agent(WebWorkerClient&, Web::HTML::WorkerAgentId, Web::HTML::WorkerAgentOwnerToken);
void remove_web_content_owner(WebContentClient&);
void remove_web_worker_owner(WebWorkerClient&);
void broadcast_channel_message_from_web_content(Web::HTML::BroadcastChannelMessage const&);
private:
friend class WebWorkerClient;
WorkerProcessManager() = default;
struct WebContentOwner {
@ -54,7 +51,7 @@ private:
};
struct WebWorkerOwner {
NonnullRefPtr<Web::HTML::WebWorkerClient> client;
NonnullRefPtr<WebWorkerClient> client;
};
struct Owner {
@ -82,7 +79,7 @@ private:
struct WorkerAgent {
Web::HTML::WorkerAgentId id { 0 };
NonnullRefPtr<Web::HTML::WebWorkerClient> client;
NonnullRefPtr<WebWorkerClient> client;
Web::Bindings::AgentType agent_type { Web::Bindings::AgentType::DedicatedWorker };
Web::Bindings::WorkerType worker_type { Web::Bindings::WorkerType::Classic };
Web::Bindings::RequestCredentials credentials { Web::Bindings::RequestCredentials::SameOrigin };