From bfc9bf80d976d5fdb6ac5e95257a7295abbe2bc2 Mon Sep 17 00:00:00 2001 From: Aliaksandr Kalenik Date: Fri, 22 May 2026 16:02:42 +0200 Subject: [PATCH] Compositor+LibWebView+WebContent: Scaffold opt-in Compositor process The compositor is moving into a dedicated helper process. That requires a process to launch, channels for Browser and WebContent to talk to it over, and client proxies on each side. Land all of that as an inert scaffold first, gated behind --enable-compositor-process, so the default rendering path is unchanged and later commits can fill in the protocol, the service-side state, and the runtime switch against a stable target. --- Libraries/LibWebView/Application.cpp | 47 ++++++++++++++++ Libraries/LibWebView/Application.h | 3 ++ Libraries/LibWebView/CMakeLists.txt | 11 ++++ Libraries/LibWebView/CompositorClient.cpp | 31 +++++++++++ Libraries/LibWebView/CompositorClient.h | 35 ++++++++++++ Libraries/LibWebView/Forward.h | 1 + Libraries/LibWebView/HelperProcess.cpp | 20 +++++++ Libraries/LibWebView/HelperProcess.h | 1 + Libraries/LibWebView/Options.h | 6 +++ Libraries/LibWebView/ProcessManager.cpp | 4 ++ Libraries/LibWebView/ProcessType.h | 1 + Libraries/LibWebView/WebContentClient.cpp | 5 ++ Libraries/LibWebView/WebContentClient.h | 3 ++ Services/CMakeLists.txt | 1 + Services/Compositor/CMakeLists.txt | 28 ++++++++++ .../Compositor/CompositorControlClient.ipc | 4 ++ .../Compositor/CompositorControlServer.ipc | 8 +++ .../Compositor/CompositorWebContentClient.ipc | 4 ++ .../Compositor/CompositorWebContentServer.ipc | 4 ++ Services/Compositor/ConnectionFromClient.cpp | 47 ++++++++++++++++ Services/Compositor/ConnectionFromClient.h | 35 ++++++++++++ .../Compositor/ConnectionFromWebContent.cpp | 26 +++++++++ .../Compositor/ConnectionFromWebContent.h | 31 +++++++++++ Services/Compositor/Forward.h | 14 +++++ Services/Compositor/main.cpp | 53 +++++++++++++++++++ Services/WebContent/CMakeLists.txt | 1 + Services/WebContent/CompositorConnection.cpp | 24 +++++++++ Services/WebContent/CompositorConnection.h | 29 ++++++++++ Services/WebContent/ConnectionFromClient.cpp | 6 +++ Services/WebContent/ConnectionFromClient.h | 3 ++ Services/WebContent/Forward.h | 1 + Services/WebContent/WebContentServer.ipc | 1 + UI/CMakeLists.txt | 2 +- 33 files changed, 489 insertions(+), 1 deletion(-) create mode 100644 Libraries/LibWebView/CompositorClient.cpp create mode 100644 Libraries/LibWebView/CompositorClient.h create mode 100644 Services/Compositor/CMakeLists.txt create mode 100644 Services/Compositor/CompositorControlClient.ipc create mode 100644 Services/Compositor/CompositorControlServer.ipc create mode 100644 Services/Compositor/CompositorWebContentClient.ipc create mode 100644 Services/Compositor/CompositorWebContentServer.ipc create mode 100644 Services/Compositor/ConnectionFromClient.cpp create mode 100644 Services/Compositor/ConnectionFromClient.h create mode 100644 Services/Compositor/ConnectionFromWebContent.cpp create mode 100644 Services/Compositor/ConnectionFromWebContent.h create mode 100644 Services/Compositor/Forward.h create mode 100644 Services/Compositor/main.cpp create mode 100644 Services/WebContent/CompositorConnection.cpp create mode 100644 Services/WebContent/CompositorConnection.h diff --git a/Libraries/LibWebView/Application.cpp b/Libraries/LibWebView/Application.cpp index 4088d1ab90..e34868026f 100644 --- a/Libraries/LibWebView/Application.cpp +++ b/Libraries/LibWebView/Application.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -97,6 +98,8 @@ Application::~Application() // Explicitly delete the observers first, as the observer destructors will refer to Application::the(). m_settings_observer.clear(); m_bookmark_store_observer.clear(); + if (m_compositor_client) + m_compositor_client->on_death = nullptr; s_the = nullptr; } @@ -164,6 +167,7 @@ ErrorOr Application::initialize(Main::Arguments const& arguments) bool disable_http_memory_cache = false; bool disable_http_disk_cache = false; bool disable_content_blocker = false; + bool enable_compositor_process = false; Vector content_blocker_list_paths; Optional resource_substitution_map_path; bool enable_autoplay = false; @@ -238,6 +242,7 @@ ErrorOr Application::initialize(Main::Arguments const& arguments) args_parser.add_option(disable_http_memory_cache, "Disable HTTP memory cache", "disable-http-memory-cache"); args_parser.add_option(disable_http_disk_cache, "Disable HTTP disk cache", "disable-http-disk-cache"); args_parser.add_option(disable_content_blocker, "Disable content blocker", "disable-content-blocker"); + args_parser.add_option(enable_compositor_process, "Enable the out-of-process compositor", "enable-compositor-process"); args_parser.add_option(Core::ArgsParser::Option { .argument_mode = Core::ArgsParser::OptionArgumentMode::Required, .help_string = "Path to a content blocker list. May be specified multiple times.", @@ -353,6 +358,7 @@ ErrorOr Application::initialize(Main::Arguments const& arguments) : OptionalNone()), .devtools_port = devtools_port, .enable_content_blocker = disable_content_blocker ? EnableContentBlocker::No : EnableContentBlocker::Yes, + .enable_compositor_process = enable_compositor_process ? EnableCompositorProcess::Yes : EnableCompositorProcess::No, .content_blocker_list_paths = move(content_blocker_list_paths_as_byte_strings), }; @@ -481,10 +487,26 @@ static ErrorOr> create_web_content_client(Option client->async_connect_to_request_server(move(request_server_handle)); client->async_connect_to_image_decoder(move(image_decoder_handle)); + TRY(Application::the().connect_web_content_to_compositor(*client)); return client; } +ErrorOr Application::connect_web_content_to_compositor(WebContentClient& web_content_client) +{ + if (m_browser_options.enable_compositor_process == EnableCompositorProcess::No) + return {}; + if (web_content_client.compositor_connection_id({}).has_value()) + return {}; + + VERIFY(m_compositor_client); + auto response = m_compositor_client->connect_web_content(); + + web_content_client.set_compositor_connection_id({}, response.web_content_connection_id()); + web_content_client.async_connect_to_compositor_process(response.take_handle()); + return {}; +} + ErrorOr> Application::launch_web_content_process(ViewImplementation& view) { if (m_spare_web_content_process) { @@ -582,6 +604,8 @@ ErrorOr Application::launch_services() TRY(launch_request_server()); TRY(launch_image_decoder_server()); + if (m_browser_options.enable_compositor_process == EnableCompositorProcess::Yes) + TRY(launch_compositor_process()); if (m_browser_options.devtools_port.has_value()) TRY(launch_devtools_server()); @@ -589,6 +613,23 @@ ErrorOr Application::launch_services() return {}; } +ErrorOr Application::launch_compositor_process() +{ + VERIFY(!m_compositor_client); + m_compositor_client = TRY(WebView::launch_compositor_process()); + m_compositor_client->on_death = [this]() { + m_compositor_client = nullptr; + + if (Core::EventLoop::current().was_exit_requested()) + return; + + dbgln("Compositor process died"); + VERIFY_NOT_REACHED(); + }; + + return {}; +} + ErrorOr Application::launch_request_server() { m_request_server_client = TRY(launch_request_server_process()); @@ -806,6 +847,12 @@ void Application::process_did_exit(Process&& process, Optional exit_status) dbgln_if(WEBVIEW_PROCESS_DEBUG, "Process {} died, type: {}", process.pid(), process_name_from_type(process.type())); switch (process.type()) { + case ProcessType::Compositor: + if (auto client = process.client(); client.has_value()) { + if (auto on_death = move(client->on_death)) + on_death(); + } + break; case ProcessType::ImageDecoder: if (auto client = process.client(); client.has_value()) { dbgln_if(WEBVIEW_PROCESS_DEBUG, "Restart ImageDecoder process"); diff --git a/Libraries/LibWebView/Application.h b/Libraries/LibWebView/Application.h index a3519881b5..94f170d8cc 100644 --- a/Libraries/LibWebView/Application.h +++ b/Libraries/LibWebView/Application.h @@ -82,6 +82,7 @@ public: #endif ErrorOr> launch_web_content_process(ViewImplementation&); + ErrorOr connect_web_content_to_compositor(WebContentClient&); virtual Optional active_web_view() const { return {}; } virtual Optional open_blank_new_tab(Web::HTML::ActivateTab) const { return {}; } @@ -207,6 +208,7 @@ protected: private: ErrorOr launch_services(); void launch_spare_web_content_process(); + ErrorOr launch_compositor_process(); ErrorOr launch_request_server(); ErrorOr launch_image_decoder_server(); ErrorOr launch_devtools_server(); @@ -277,6 +279,7 @@ private: RefPtr m_request_server_client; RefPtr m_image_decoder_client; + RefPtr m_compositor_client; RefPtr m_spare_web_content_process; bool m_has_queued_task_to_launch_spare_web_content_process { false }; diff --git a/Libraries/LibWebView/CMakeLists.txt b/Libraries/LibWebView/CMakeLists.txt index 1886d7e7e2..d6751ee945 100644 --- a/Libraries/LibWebView/CMakeLists.txt +++ b/Libraries/LibWebView/CMakeLists.txt @@ -33,6 +33,7 @@ set(SOURCES WebUI/ProcessesUI.cpp WebUI/SettingsUI.cpp WebUI/VersionUI.cpp + CompositorClient.cpp ) set(GENERATED_SOURCES ${CURRENT_LIB_GENERATED}) @@ -48,12 +49,18 @@ embed_as_string( compile_ipc(UIProcessServer.ipc UIProcessServerEndpoint.h) compile_ipc(UIProcessClient.ipc UIProcessClientEndpoint.h) +file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/Services/Compositor) + if (NOT APPLE AND NOT CMAKE_INSTALL_LIBEXECDIR STREQUAL "libexec") set_source_files_properties(Utilities.cpp PROPERTIES COMPILE_DEFINITIONS LADYBIRD_LIBEXECDIR="${CMAKE_INSTALL_LIBEXECDIR}") endif() set(GENERATED_SOURCES ${GENERATED_SOURCES} + ../../Services/Compositor/CompositorControlClientEndpoint.h + ../../Services/Compositor/CompositorControlServerEndpoint.h + ../../Services/Compositor/CompositorWebContentClientEndpoint.h + ../../Services/Compositor/CompositorWebContentServerEndpoint.h ../../Services/RequestServer/RequestClientEndpoint.h ../../Services/RequestServer/RequestServerEndpoint.h ../../Services/WebContent/CompositorClientEndpoint.h @@ -71,6 +78,10 @@ set(GENERATED_SOURCES UIProcessServerEndpoint.h ) +compile_ipc(${LADYBIRD_SOURCE_DIR}/Services/Compositor/CompositorControlClient.ipc ${CMAKE_BINARY_DIR}/Services/Compositor/CompositorControlClientEndpoint.h) +compile_ipc(${LADYBIRD_SOURCE_DIR}/Services/Compositor/CompositorControlServer.ipc ${CMAKE_BINARY_DIR}/Services/Compositor/CompositorControlServerEndpoint.h) +compile_ipc(${LADYBIRD_SOURCE_DIR}/Services/Compositor/CompositorWebContentClient.ipc ${CMAKE_BINARY_DIR}/Services/Compositor/CompositorWebContentClientEndpoint.h) +compile_ipc(${LADYBIRD_SOURCE_DIR}/Services/Compositor/CompositorWebContentServer.ipc ${CMAKE_BINARY_DIR}/Services/Compositor/CompositorWebContentServerEndpoint.h) compile_ipc(${LADYBIRD_SOURCE_DIR}/Services/WebContent/CompositorClient.ipc ${CMAKE_BINARY_DIR}/Services/WebContent/CompositorClientEndpoint.h) compile_ipc(${LADYBIRD_SOURCE_DIR}/Services/WebContent/CompositorServer.ipc ${CMAKE_BINARY_DIR}/Services/WebContent/CompositorServerEndpoint.h) compile_ipc(${LADYBIRD_SOURCE_DIR}/Services/WebContent/WebContentClient.ipc ${CMAKE_BINARY_DIR}/Services/WebContent/WebContentClientEndpoint.h) diff --git a/Libraries/LibWebView/CompositorClient.cpp b/Libraries/LibWebView/CompositorClient.cpp new file mode 100644 index 0000000000..dd2549685f --- /dev/null +++ b/Libraries/LibWebView/CompositorClient.cpp @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2026, the Ladybird developers. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include + +#include + +namespace WebView { + +CompositorClient::CompositorClient(NonnullOwnPtr transport) + : IPC::ConnectionToServer(*this, move(transport)) +{ +} + +void CompositorClient::die() +{ + if (auto callback = move(on_death)) { + Core::deferred_invoke([callback = move(callback)]() mutable { + callback(); + }); + } +} + +void CompositorClient::did_connect_web_content(i32) +{ +} + +} diff --git a/Libraries/LibWebView/CompositorClient.h b/Libraries/LibWebView/CompositorClient.h new file mode 100644 index 0000000000..845ea1dd9f --- /dev/null +++ b/Libraries/LibWebView/CompositorClient.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2026, the Ladybird developers. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include +#include + +namespace WebView { + +class WEBVIEW_API CompositorClient final + : public IPC::ConnectionToServer + , public CompositorControlClientEndpoint { + C_OBJECT_ABSTRACT(CompositorClient) + +public: + using InitTransport = Messages::CompositorControlServer::InitTransport; + + explicit CompositorClient(NonnullOwnPtr); + + Function on_death; + +private: + virtual void die() override; + + virtual void did_connect_web_content(i32 web_content_connection_id) override; +}; + +} diff --git a/Libraries/LibWebView/Forward.h b/Libraries/LibWebView/Forward.h index 0a6c391ab8..50da5a6d84 100644 --- a/Libraries/LibWebView/Forward.h +++ b/Libraries/LibWebView/Forward.h @@ -16,6 +16,7 @@ class Action; class Application; class Autocomplete; class BookmarkStore; +class CompositorClient; class CookieJar; class HistoryStore; class Menu; diff --git a/Libraries/LibWebView/HelperProcess.cpp b/Libraries/LibWebView/HelperProcess.cpp index 613b0d850a..e66c099926 100644 --- a/Libraries/LibWebView/HelperProcess.cpp +++ b/Libraries/LibWebView/HelperProcess.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -165,6 +166,25 @@ ErrorOr> launch_image_decoder_process( return launch_server_process("ImageDecoder"sv, arguments); } +ErrorOr> launch_compositor_process() +{ + auto const& web_content_options = WebView::Application::web_content_options(); + + Vector arguments; + if (web_content_options.force_cpu_painting == WebView::ForceCPUPainting::Yes) + arguments.append("--force-cpu-painting"sv); + if (web_content_options.force_fontconfig == WebView::ForceFontconfig::Yes) + arguments.append("--force-fontconfig"sv); + if (web_content_options.enable_async_scrolling == EnableAsyncScrolling::No) + arguments.append("--disable-async-scrolling"sv); + if (auto server = mach_server_name(); server.has_value()) { + arguments.append("--mach-server-name"sv); + arguments.append(server.value()); + } + + return launch_server_process("Compositor"sv, move(arguments)); +} + ErrorOr> launch_web_worker_process(Web::Bindings::AgentType type) { auto const& web_content_options = WebView::Application::web_content_options(); diff --git a/Libraries/LibWebView/HelperProcess.h b/Libraries/LibWebView/HelperProcess.h index 906bc87c68..2b349683d4 100644 --- a/Libraries/LibWebView/HelperProcess.h +++ b/Libraries/LibWebView/HelperProcess.h @@ -24,6 +24,7 @@ WEBVIEW_API ErrorOr> launch_web_content WEBVIEW_API ErrorOr> launch_spare_web_content_process(); WEBVIEW_API ErrorOr> launch_image_decoder_process(); +WEBVIEW_API ErrorOr> launch_compositor_process(); WEBVIEW_API ErrorOr> launch_web_worker_process(Web::Bindings::AgentType); WEBVIEW_API ErrorOr> launch_request_server_process(); diff --git a/Libraries/LibWebView/Options.h b/Libraries/LibWebView/Options.h index c8cfb96f1d..968ab77429 100644 --- a/Libraries/LibWebView/Options.h +++ b/Libraries/LibWebView/Options.h @@ -74,6 +74,11 @@ enum class EnableContentBlocker { Yes, }; +enum class EnableCompositorProcess { + No, + Yes, +}; + struct BrowserOptions { Vector urls; Vector raw_urls; @@ -92,6 +97,7 @@ struct BrowserOptions { Optional dns_settings {}; Optional devtools_port; EnableContentBlocker enable_content_blocker { EnableContentBlocker::Yes }; + EnableCompositorProcess enable_compositor_process { EnableCompositorProcess::No }; Vector content_blocker_list_paths {}; }; diff --git a/Libraries/LibWebView/ProcessManager.cpp b/Libraries/LibWebView/ProcessManager.cpp index e8ebb97a72..b3a6efa09e 100644 --- a/Libraries/LibWebView/ProcessManager.cpp +++ b/Libraries/LibWebView/ProcessManager.cpp @@ -17,6 +17,8 @@ ProcessType process_type_from_name(StringView name) { if (name == "Browser"sv) return ProcessType::Browser; + if (name == "Compositor"sv) + return ProcessType::Compositor; if (name == "WebContent"sv) return ProcessType::WebContent; if (name == "WebWorker"sv) @@ -35,6 +37,8 @@ StringView process_name_from_type(ProcessType type) switch (type) { case ProcessType::Browser: return "Browser"sv; + case ProcessType::Compositor: + return "Compositor"sv; case ProcessType::WebContent: return "WebContent"sv; case ProcessType::WebWorker: diff --git a/Libraries/LibWebView/ProcessType.h b/Libraries/LibWebView/ProcessType.h index bdfab6b735..1b0fd78f2d 100644 --- a/Libraries/LibWebView/ProcessType.h +++ b/Libraries/LibWebView/ProcessType.h @@ -12,6 +12,7 @@ namespace WebView { enum class ProcessType : u8 { Browser, + Compositor, WebContent, WebWorker, RequestServer, diff --git a/Libraries/LibWebView/WebContentClient.cpp b/Libraries/LibWebView/WebContentClient.cpp index 9e5baffca4..5b9fdbac32 100644 --- a/Libraries/LibWebView/WebContentClient.cpp +++ b/Libraries/LibWebView/WebContentClient.cpp @@ -128,6 +128,11 @@ void WebContentClient::assign_view(Badge, ViewImplementation& view) m_views.set(0, view); } +void WebContentClient::set_compositor_connection_id(Badge, i32 compositor_connection_id) +{ + m_compositor_connection_id = compositor_connection_id; +} + void WebContentClient::register_view(u64 page_id, ViewImplementation& view) { VERIFY(page_id > 0); diff --git a/Libraries/LibWebView/WebContentClient.h b/Libraries/LibWebView/WebContentClient.h index 441a84b984..d43376c157 100644 --- a/Libraries/LibWebView/WebContentClient.h +++ b/Libraries/LibWebView/WebContentClient.h @@ -54,6 +54,8 @@ public: ~WebContentClient(); void assign_view(Badge, ViewImplementation&); + void set_compositor_connection_id(Badge, i32); + Optional compositor_connection_id(Badge) const { return m_compositor_connection_id; } void register_view(u64 page_id, ViewImplementation&); void unregister_view(u64 page_id); @@ -168,6 +170,7 @@ private: HashMap> m_views; HashMap m_history_recorded_urls_for_current_load; + Optional m_compositor_connection_id; ProcessHandle m_process_handle; diff --git a/Services/CMakeLists.txt b/Services/CMakeLists.txt index 2b6f5844aa..67c9ee208f 100644 --- a/Services/CMakeLists.txt +++ b/Services/CMakeLists.txt @@ -1,3 +1,4 @@ +add_subdirectory(Compositor) add_subdirectory(ImageDecoder) add_subdirectory(RequestServer) add_subdirectory(WebContent) diff --git a/Services/Compositor/CMakeLists.txt b/Services/Compositor/CMakeLists.txt new file mode 100644 index 0000000000..fd12046c34 --- /dev/null +++ b/Services/Compositor/CMakeLists.txt @@ -0,0 +1,28 @@ +set(SOURCES + ConnectionFromClient.cpp + ConnectionFromWebContent.cpp +) + +set(GENERATED_SOURCES + CompositorControlClientEndpoint.h + CompositorControlServerEndpoint.h + CompositorWebContentClientEndpoint.h + CompositorWebContentServerEndpoint.h +) + +add_library(compositorservice STATIC ${SOURCES} ${GENERATED_SOURCES}) +ladybird_generated_sources(compositorservice) + +add_executable(Compositor main.cpp) + +target_include_directories(compositorservice PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/../..) +target_include_directories(compositorservice PRIVATE ${LADYBIRD_SOURCE_DIR}/Services/) + +target_link_libraries(Compositor PRIVATE compositorservice LibCore LibGfx LibIPC LibMain LibWebView) +target_link_libraries(compositorservice PRIVATE LibCore LibIPC) + +if (WIN32) + target_include_directories(Compositor PRIVATE $) + target_include_directories(compositorservice PRIVATE $) + ladybird_windows_bin(Compositor CONSOLE) +endif() diff --git a/Services/Compositor/CompositorControlClient.ipc b/Services/Compositor/CompositorControlClient.ipc new file mode 100644 index 0000000000..e7610fa623 --- /dev/null +++ b/Services/Compositor/CompositorControlClient.ipc @@ -0,0 +1,4 @@ +endpoint CompositorControlClient +{ + did_connect_web_content(i32 web_content_connection_id) =| +} diff --git a/Services/Compositor/CompositorControlServer.ipc b/Services/Compositor/CompositorControlServer.ipc new file mode 100644 index 0000000000..8e294f1417 --- /dev/null +++ b/Services/Compositor/CompositorControlServer.ipc @@ -0,0 +1,8 @@ +#include + +endpoint CompositorControlServer +{ + init_transport(int peer_pid) => (int peer_pid) + + connect_web_content() => (IPC::TransportHandle handle, i32 web_content_connection_id) +} diff --git a/Services/Compositor/CompositorWebContentClient.ipc b/Services/Compositor/CompositorWebContentClient.ipc new file mode 100644 index 0000000000..1cdc734c8c --- /dev/null +++ b/Services/Compositor/CompositorWebContentClient.ipc @@ -0,0 +1,4 @@ +endpoint CompositorWebContentClient +{ + did_connect() =| +} diff --git a/Services/Compositor/CompositorWebContentServer.ipc b/Services/Compositor/CompositorWebContentServer.ipc new file mode 100644 index 0000000000..dda46995cf --- /dev/null +++ b/Services/Compositor/CompositorWebContentServer.ipc @@ -0,0 +1,4 @@ +endpoint CompositorWebContentServer +{ + ping() =| +} diff --git a/Services/Compositor/ConnectionFromClient.cpp b/Services/Compositor/ConnectionFromClient.cpp new file mode 100644 index 0000000000..0731fdd469 --- /dev/null +++ b/Services/Compositor/ConnectionFromClient.cpp @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2026, the Ladybird developers. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include + +namespace Compositor { + +ConnectionFromClient::ConnectionFromClient(NonnullOwnPtr transport) + : IPC::ConnectionFromClient(*this, move(transport), 1) +{ +} + +void ConnectionFromClient::die() +{ + Core::EventLoop::current().quit(0); +} + +Messages::CompositorControlServer::InitTransportResponse ConnectionFromClient::init_transport([[maybe_unused]] int peer_pid) +{ +#ifdef AK_OS_WINDOWS + m_transport->set_peer_pid(peer_pid); + return Core::System::getpid(); +#endif + VERIFY_NOT_REACHED(); +} + +Messages::CompositorControlServer::ConnectWebContentResponse ConnectionFromClient::connect_web_content() +{ + auto paired_transport = MUST(IPC::Transport::create_paired()); + auto web_content_connection_id = m_next_web_content_connection_id++; + auto connection = ConnectionFromWebContent::construct(move(paired_transport.local), web_content_connection_id); + connection->on_death = [this](auto& dead) { + m_web_content_connections.remove(dead.client_id()); + }; + m_web_content_connections.set(web_content_connection_id, move(connection)); + async_did_connect_web_content(web_content_connection_id); + return { move(paired_transport.remote_handle), web_content_connection_id }; +} + +} diff --git a/Services/Compositor/ConnectionFromClient.h b/Services/Compositor/ConnectionFromClient.h new file mode 100644 index 0000000000..a60c842d4b --- /dev/null +++ b/Services/Compositor/ConnectionFromClient.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2026, the Ladybird developers. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace Compositor { + +class ConnectionFromClient final + : public IPC::ConnectionFromClient { + C_OBJECT(ConnectionFromClient) + +public: + virtual void die() override; + +private: + explicit ConnectionFromClient(NonnullOwnPtr); + + virtual Messages::CompositorControlServer::InitTransportResponse init_transport(int peer_pid) override; + virtual Messages::CompositorControlServer::ConnectWebContentResponse connect_web_content() override; + + i32 m_next_web_content_connection_id { 1 }; + HashMap> m_web_content_connections; +}; + +} diff --git a/Services/Compositor/ConnectionFromWebContent.cpp b/Services/Compositor/ConnectionFromWebContent.cpp new file mode 100644 index 0000000000..12fd4f6803 --- /dev/null +++ b/Services/Compositor/ConnectionFromWebContent.cpp @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2026, the Ladybird developers. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include + +namespace Compositor { + +ConnectionFromWebContent::ConnectionFromWebContent(NonnullOwnPtr transport, int client_id) + : IPC::ConnectionFromClient(*this, move(transport), client_id) +{ +} + +void ConnectionFromWebContent::die() +{ + if (on_death) + on_death(*this); +} + +void ConnectionFromWebContent::ping() +{ +} + +} diff --git a/Services/Compositor/ConnectionFromWebContent.h b/Services/Compositor/ConnectionFromWebContent.h new file mode 100644 index 0000000000..bf0bc80976 --- /dev/null +++ b/Services/Compositor/ConnectionFromWebContent.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2026, the Ladybird developers. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include + +namespace Compositor { + +class ConnectionFromWebContent final + : public IPC::ConnectionFromClient { + C_OBJECT(ConnectionFromWebContent) + +public: + virtual void die() override; + + Function on_death; + +private: + explicit ConnectionFromWebContent(NonnullOwnPtr, int client_id); + + virtual void ping() override; +}; + +} diff --git a/Services/Compositor/Forward.h b/Services/Compositor/Forward.h new file mode 100644 index 0000000000..b5d6d415b9 --- /dev/null +++ b/Services/Compositor/Forward.h @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2026, the Ladybird developers. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +namespace Compositor { + +class ConnectionFromClient; +class ConnectionFromWebContent; + +} diff --git a/Services/Compositor/main.cpp b/Services/Compositor/main.cpp new file mode 100644 index 0000000000..f212cadc26 --- /dev/null +++ b/Services/Compositor/main.cpp @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2026, the Ladybird developers. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +ErrorOr ladybird_main(Main::Arguments arguments) +{ + AK::set_rich_debug_enabled(true); + + StringView mach_server_name; + bool wait_for_debugger = false; + bool force_cpu_painting = false; + bool force_fontconfig = false; + bool disable_async_scrolling = false; + + Core::ArgsParser args_parser; + args_parser.add_option(mach_server_name, "Mach server name", "mach-server-name", 0, "mach_server_name"); + args_parser.add_option(wait_for_debugger, "Wait for debugger", "wait-for-debugger"); + args_parser.add_option(force_cpu_painting, "Force CPU painting", "force-cpu-painting"); + args_parser.add_option(force_fontconfig, "Force using fontconfig for font loading", "force-fontconfig"); + args_parser.add_option(disable_async_scrolling, "Disable async scrolling", "disable-async-scrolling"); + args_parser.parse(arguments); + + if (wait_for_debugger) + Core::Process::wait_for_debugger_and_break(); + + WebView::platform_init(); + auto& font_provider = static_cast(Gfx::FontDatabase::the().install_system_font_provider(make())); + if (force_fontconfig) + font_provider.set_name_but_fixme_should_create_custom_system_font_provider("FontConfig"_string); + + if (!force_cpu_painting) + Gfx::SkiaBackendContext::initialize_gpu_backend(); + + Core::EventLoop event_loop; + auto client = TRY(IPC::take_over_accepted_client_from_system_server(mach_server_name)); + (void)client; + (void)disable_async_scrolling; + + return event_loop.exec(); +} diff --git a/Services/WebContent/CMakeLists.txt b/Services/WebContent/CMakeLists.txt index 19b13ea71c..be2dd1256d 100644 --- a/Services/WebContent/CMakeLists.txt +++ b/Services/WebContent/CMakeLists.txt @@ -1,6 +1,7 @@ include(audio) set(SOURCES + CompositorConnection.cpp ConnectionFromClient.cpp ConsoleGlobalEnvironmentExtensions.cpp DevToolsConsoleClient.cpp diff --git a/Services/WebContent/CompositorConnection.cpp b/Services/WebContent/CompositorConnection.cpp new file mode 100644 index 0000000000..2b75e63ee1 --- /dev/null +++ b/Services/WebContent/CompositorConnection.cpp @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2026, the Ladybird developers. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include + +namespace WebContent { + +CompositorConnection::CompositorConnection(NonnullOwnPtr transport) + : IPC::ConnectionToServer(*this, move(transport)) +{ +} + +void CompositorConnection::die() +{ +} + +void CompositorConnection::did_connect() +{ +} + +} diff --git a/Services/WebContent/CompositorConnection.h b/Services/WebContent/CompositorConnection.h new file mode 100644 index 0000000000..55615cfc3c --- /dev/null +++ b/Services/WebContent/CompositorConnection.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2026, the Ladybird developers. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include + +namespace WebContent { + +class CompositorConnection final + : public IPC::ConnectionToServer + , public CompositorWebContentClientEndpoint { + C_OBJECT_ABSTRACT(CompositorConnection) + +public: + explicit CompositorConnection(NonnullOwnPtr); + +private: + virtual void die() override; + + virtual void did_connect() override; +}; + +} diff --git a/Services/WebContent/ConnectionFromClient.cpp b/Services/WebContent/ConnectionFromClient.cpp index 89187382bb..79a28a8139 100644 --- a/Services/WebContent/ConnectionFromClient.cpp +++ b/Services/WebContent/ConnectionFromClient.cpp @@ -61,6 +61,7 @@ #include #include #include +#include #include #include #include @@ -155,6 +156,11 @@ void ConnectionFromClient::connect_to_compositor(IPC::TransportHandle handle) m_page_host->attach_compositor_ui_client(move(handle)); } +void ConnectionFromClient::connect_to_compositor_process(IPC::TransportHandle handle) +{ + (void)handle; +} + void ConnectionFromClient::connect_to_request_server(IPC::TransportHandle handle) { if (on_request_server_connection) diff --git a/Services/WebContent/ConnectionFromClient.h b/Services/WebContent/ConnectionFromClient.h index dacbc8bca1..4ecc87ca73 100644 --- a/Services/WebContent/ConnectionFromClient.h +++ b/Services/WebContent/ConnectionFromClient.h @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -68,6 +69,7 @@ private: virtual void connect_to_request_server(IPC::TransportHandle handle) override; virtual void connect_to_image_decoder(IPC::TransportHandle handle) override; virtual void connect_to_compositor(IPC::TransportHandle handle) override; + virtual void connect_to_compositor_process(IPC::TransportHandle handle) override; virtual void update_system_theme(u64 page_id, Core::AnonymousBuffer) override; virtual void update_screen_rects(u64 page_id, Vector, u32) override; virtual void load_url(u64 page_id, URL::URL) override; @@ -185,6 +187,7 @@ private: void enqueue_input_event(Web::QueuedInputEvent); Queue m_input_event_queue; + RefPtr m_compositor_connection; }; } diff --git a/Services/WebContent/Forward.h b/Services/WebContent/Forward.h index 5724e323b5..6e3f2325d6 100644 --- a/Services/WebContent/Forward.h +++ b/Services/WebContent/Forward.h @@ -8,6 +8,7 @@ namespace WebContent { +class CompositorConnection; class ConnectionFromClient; class ConsoleGlobalEnvironmentExtensions; class DevToolsConsoleClient; diff --git a/Services/WebContent/WebContentServer.ipc b/Services/WebContent/WebContentServer.ipc index 1478492fc7..38a8e71a32 100644 --- a/Services/WebContent/WebContentServer.ipc +++ b/Services/WebContent/WebContentServer.ipc @@ -35,6 +35,7 @@ endpoint WebContentServer connect_to_request_server(IPC::TransportHandle handle) =| connect_to_image_decoder(IPC::TransportHandle handle) =| connect_to_compositor(IPC::TransportHandle handle) =| + connect_to_compositor_process(IPC::TransportHandle handle) =| update_system_theme(u64 page_id, Core::AnonymousBuffer theme_buffer) =| update_screen_rects(u64 page_id, Vector rects, u32 main_screen_index) =| diff --git a/UI/CMakeLists.txt b/UI/CMakeLists.txt index 7a0e960a81..0f71d3d7ad 100644 --- a/UI/CMakeLists.txt +++ b/UI/CMakeLists.txt @@ -99,7 +99,7 @@ else() ) endif() -set(ladybird_helper_processes ImageDecoder RequestServer WebContent WebWorker) +set(ladybird_helper_processes Compositor ImageDecoder RequestServer WebContent WebWorker) add_dependencies(ladybird ${ladybird_helper_processes}) # FIXME: Increase support for building targets on Windows