LibWebView+WebContent: Add feature-gated Compositor process backend

We are moving toward an architecture where the browser owns a single
process that holds the GPU context, so Skia resources can be shared
across every renderer and WebContent processes can be sandboxed away
from direct GPU access. A dedicated Compositor helper process is the
first step. The earlier commits laid the foundation; this one turns the
helper on as a selectable backend, gated behind
--enable-compositor-process so the existing in-process path stays the
shipping default.

Default topology -- one compositor per WebContent, in-process:

      +---------+
      | Browser |
      +---------+
      /    |    \
     v     v     v
   +---+ +---+ +---+    each WebContent runs its own
   |WC | |WC | |WC |    CompositorThread on a dedicated
   |+C+| |+C+| |+C+|    thread, with its own GPU context.
   +---+ +---+ +---+

Opt-in topology -- one single-threaded Compositor, shared by all WCs:

   +---------+   control   +-------------------+
   | Browser |<----------->|    Compositor     |
   +---------+             |  (single thread,  |
   /    |    \             |   single GPU ctx, |
  v     v     v    data    |   shared by all   |
 [WC] [WC] [WC] ---------->|   connected WCs)  |
                           +-------------------+

Three IPC channels carry the work in the opt-in topology. The
existing Browser<->WebContent channel gains context allocation.
A new Browser<->Compositor channel carries context lifetime,
viewport, UI input, and presentation acks plus backing-store and
frame upcalls. A new WebContent<->Compositor channel carries
display lists, scroll state, video, compositor surfaces, async
scrolling, presentation, and screenshots, with upcalls for
delegated input and compositor loss.
This commit is contained in:
Aliaksandr Kalenik 2026-05-22 16:17:27 +02:00 committed by Alexander Kalenik
parent 3eae2c0f3e
commit 9479bc8d7f
18 changed files with 306 additions and 48 deletions

View file

@ -20,6 +20,7 @@
#include <LibImageDecoderClient/Client.h>
#include <LibWeb/CSS/PropertyID.h>
#include <LibWeb/Loader/UserAgent.h>
#include <LibWeb/Page/InputEvent.h>
#include <LibWebView/Application.h>
#include <LibWebView/CompositorClient.h>
#include <LibWebView/CookieJar.h>
@ -532,13 +533,43 @@ void Application::register_compositor_context(WebContentClient& web_content_clie
m_compositor_client->create_context(context_id, page_id, page_presentation_registration, *web_content_connection_id);
}
void Application::destroy_compositor_context(Web::Compositor::CompositorContextId context_id)
void Application::update_compositor_viewport(Web::Compositor::CompositorContextId context_id, Gfx::IntSize viewport_size, Web::Compositor::WindowResizingInProgress window_resize_in_progress)
{
if (should_skip_compositor_process_ipc(m_compositor_client))
return;
VERIFY(m_compositor_client);
m_compositor_client->async_destroy_context(context_id);
m_compositor_client->async_viewport_size_updated(context_id, viewport_size, true, window_resize_in_progress);
}
bool Application::send_async_scroll_to_compositor(Web::Compositor::CompositorContextId context_id, Gfx::FloatPoint position, Gfx::FloatPoint delta_in_device_pixels)
{
VERIFY(m_compositor_client);
return m_compositor_client->async_scroll_by(context_id, position, delta_in_device_pixels);
}
bool Application::handle_mouse_event_in_compositor(Web::Compositor::CompositorContextId context_id, Web::MouseEvent const& event)
{
VERIFY(m_compositor_client);
return m_compositor_client->handle_mouse_event(context_id, event.clone_without_browser_data());
}
void Application::dispatch_mouse_event_to_web_content(Web::Compositor::CompositorContextId context_id, Web::MouseEvent const& event)
{
if (should_skip_compositor_process_ipc(m_compositor_client))
return;
VERIFY(m_compositor_client);
m_compositor_client->async_dispatch_mouse_event_to_web_content(context_id, event.clone_without_browser_data());
}
void Application::notify_compositor_presented_bitmap_ready_to_paint(Web::Compositor::CompositorContextId context_id, i32 bitmap_id)
{
if (should_skip_compositor_process_ipc(m_compositor_client))
return;
VERIFY(m_compositor_client);
m_compositor_client->async_presented_bitmap_ready_to_paint(context_id, bitmap_id);
}
ErrorOr<NonnullRefPtr<WebContentClient>> Application::launch_web_content_process(ViewImplementation& view)

View file

@ -16,6 +16,8 @@
#include <LibDatabase/Forward.h>
#include <LibDevTools/DevToolsDelegate.h>
#include <LibDevTools/Forward.h>
#include <LibGfx/Point.h>
#include <LibGfx/Size.h>
#include <LibIPC/Forward.h>
#include <LibImageDecoderClient/Client.h>
#include <LibMain/Main.h>
@ -40,6 +42,12 @@
# include <LibIPC/TransportBootstrapMach.h>
#endif
namespace Web {
struct MouseEvent;
}
namespace WebView {
struct ApplicationSettingsObserver;
@ -85,7 +93,11 @@ public:
ErrorOr<NonnullRefPtr<WebContentClient>> launch_web_content_process(ViewImplementation&);
ErrorOr<void> connect_web_content_to_compositor(WebContentClient&);
void register_compositor_context(WebContentClient&, Web::Compositor::CompositorContextId, Optional<u64> page_id, Web::Compositor::PagePresentationRegistration);
void destroy_compositor_context(Web::Compositor::CompositorContextId);
void update_compositor_viewport(Web::Compositor::CompositorContextId, Gfx::IntSize viewport_size, Web::Compositor::WindowResizingInProgress = Web::Compositor::WindowResizingInProgress::No);
bool send_async_scroll_to_compositor(Web::Compositor::CompositorContextId, Gfx::FloatPoint position, Gfx::FloatPoint delta_in_device_pixels);
bool handle_mouse_event_in_compositor(Web::Compositor::CompositorContextId, Web::MouseEvent const&);
void dispatch_mouse_event_to_web_content(Web::Compositor::CompositorContextId, Web::MouseEvent const&);
void notify_compositor_presented_bitmap_ready_to_paint(Web::Compositor::CompositorContextId, i32 bitmap_id);
virtual Optional<ViewImplementation&> active_web_view() const { return {}; }
virtual Optional<ViewImplementation&> open_blank_new_tab(Web::HTML::ActivateTab) const { return {}; }

View file

@ -7,6 +7,7 @@
#include <LibWebView/CompositorClient.h>
#include <LibCore/EventLoop.h>
#include <LibWebView/WebContentClient.h>
namespace WebView {
@ -24,12 +25,29 @@ void CompositorClient::die()
}
}
void CompositorClient::did_allocate_backing_stores(Web::Compositor::CompositorContextId, i32, Gfx::SharedImage, i32, Gfx::SharedImage)
void CompositorClient::did_allocate_backing_stores(Web::Compositor::CompositorContextId context_id, i32 front_bitmap_id, Gfx::SharedImage front_backing_store, i32 back_bitmap_id, Gfx::SharedImage back_backing_store)
{
auto web_content_client = WebContentClient::client_for_compositor_context_id(context_id);
if (!web_content_client.has_value())
return;
auto page_id = web_content_client->page_id_for_compositor_context_id(context_id);
VERIFY(page_id.has_value());
web_content_client->did_present_backing_stores(*page_id, front_bitmap_id, move(front_backing_store), back_bitmap_id, move(back_backing_store));
}
void CompositorClient::did_present_frame(Web::Compositor::CompositorContextId, Gfx::IntRect, i32)
void CompositorClient::did_present_frame(Web::Compositor::CompositorContextId context_id, Gfx::IntRect content_rect, i32 bitmap_id)
{
auto web_content_client = WebContentClient::client_for_compositor_context_id(context_id);
if (web_content_client.has_value()) {
auto page_id = web_content_client->page_id_for_compositor_context_id(context_id);
VERIFY(page_id.has_value());
web_content_client->did_present_bitmap(*page_id, content_rect, bitmap_id);
return;
}
async_presented_bitmap_ready_to_paint(context_id, bitmap_id);
}
}

View file

@ -60,7 +60,7 @@ HeadlessWebView::HeadlessWebView(Core::AnonymousBuffer theme, Web::DevicePixelSi
m_viewport_size = size.template to_type<Web::DevicePixels>();
client().async_set_window_size(m_client_state.page_index, m_viewport_size);
client().async_set_viewport(m_client_state.page_index, m_viewport_size, m_device_pixel_ratio, Web::ViewportIsFullscreen::No);
handle_resize();
client().async_did_update_window_rect(m_client_state.page_index);
};
@ -79,7 +79,7 @@ HeadlessWebView::HeadlessWebView(Core::AnonymousBuffer theme, Web::DevicePixelSi
client().async_set_window_position(m_client_state.page_index, screen_rect.location());
client().async_set_window_size(m_client_state.page_index, screen_rect.size());
client().async_set_viewport(m_client_state.page_index, screen_rect.size(), m_device_pixel_ratio, m_is_fullscreen);
handle_resize();
client().async_did_update_window_rect(m_client_state.page_index);
};
@ -91,7 +91,7 @@ HeadlessWebView::HeadlessWebView(Core::AnonymousBuffer theme, Web::DevicePixelSi
client().async_set_window_position(m_client_state.page_index, screen_rect.location());
client().async_set_window_size(m_client_state.page_index, screen_rect.size());
client().async_set_viewport(m_client_state.page_index, screen_rect.size(), m_device_pixel_ratio, Web::ViewportIsFullscreen::Yes);
handle_resize();
client().async_did_update_window_rect(m_client_state.page_index);
};
@ -102,7 +102,7 @@ HeadlessWebView::HeadlessWebView(Core::AnonymousBuffer theme, Web::DevicePixelSi
client().async_set_window_position(m_client_state.page_index, m_previous_dimensions.location());
client().async_set_window_size(m_client_state.page_index, m_previous_dimensions.size());
client().async_set_viewport(m_client_state.page_index, m_previous_dimensions.size(), m_device_pixel_ratio, Web::ViewportIsFullscreen::No);
handle_resize();
client().async_did_update_window_rect(m_client_state.page_index);
};
@ -171,7 +171,7 @@ void HeadlessWebView::initialize_client(CreateNewClient create_new_client)
ViewImplementation::initialize_client(create_new_client);
client().async_update_system_theme(m_client_state.page_index, m_theme);
client().async_set_viewport(m_client_state.page_index, viewport_size(), m_device_pixel_ratio, m_is_fullscreen);
handle_resize();
client().async_set_window_size(m_client_state.page_index, viewport_size());
client().async_update_screen_rects(m_client_state.page_index, { { screen_rect } }, 0);
}
@ -181,7 +181,7 @@ void HeadlessWebView::reset_viewport_size(Web::DevicePixelSize size)
m_viewport_size = size;
client().async_set_window_size(m_client_state.page_index, m_viewport_size);
client().async_set_viewport(m_client_state.page_index, m_viewport_size, m_device_pixel_ratio, m_is_fullscreen);
handle_resize();
client().async_did_update_window_rect(m_client_state.page_index);
}

View file

@ -121,6 +121,8 @@ static ErrorOr<NonnullRefPtr<WebView::WebContentClient>> launch_web_content_proc
arguments.append("--disable-scrollbar-painting"sv);
if (web_content_options.enable_async_scrolling == EnableAsyncScrolling::No)
arguments.append("--disable-async-scrolling"sv);
if (browser_options.enable_compositor_process == EnableCompositorProcess::Yes)
arguments.append("--enable-compositor-process"sv);
if (web_content_options.file_scheme_urls_have_tuple_origins == FileSchemeUrlsHaveTupleOrigins::Yes)
arguments.append("--tuple-file-origins"sv);

View file

@ -292,7 +292,7 @@ void ViewImplementation::enqueue_input_event(Web::InputEvent event)
mouse_event->async_scroll_performed_default_action = true;
dbgln_if(COMPOSITOR_DEBUG, "[Compositor] UI compositor wheel bypass result for page {}: {}",
m_client_state.page_index, mouse_event->async_scroll_performed_default_action ? "accepted"sv : "rejected"sv);
} else if (client().send_mouse_event_to_compositor(m_client_state.page_index, *mouse_event)) {
} else if (client().handle_mouse_event_in_compositor(m_client_state.page_index, *mouse_event)) {
dbgln_if(COMPOSITOR_DEBUG, "[Compositor] UI compositor handled mouse event for page {} at {},{}",
m_client_state.page_index, mouse_event->position.x().value(), mouse_event->position.y().value());
return;
@ -309,7 +309,7 @@ void ViewImplementation::enqueue_input_event(Web::InputEvent event)
client().async_key_event(m_client_state.page_index, event.clone_without_browser_data());
},
[this](Web::MouseEvent const& event) {
client().async_mouse_event(m_client_state.page_index, event.clone_without_browser_data());
client().dispatch_mouse_event_to_web_content(m_client_state.page_index, event);
},
[this](Web::DragEvent& event) {
auto cloned_event = event.clone_without_browser_data();
@ -720,6 +720,8 @@ void ViewImplementation::apply_zoom_for_current_host()
void ViewImplementation::handle_resize()
{
client().async_set_viewport(page_id(), viewport_size(), m_device_pixel_ratio, m_is_fullscreen);
if (Application::browser_options().enable_compositor_process == EnableCompositorProcess::Yes)
Application::the().update_compositor_viewport(client().compositor_context_id_for_page(page_id()), viewport_size().to_type<int>());
}
void ViewImplementation::initialize_client(CreateNewClient create_new_client)
@ -739,6 +741,10 @@ void ViewImplementation::initialize_client(CreateNewClient create_new_client)
client().async_set_viewport(m_client_state.page_index, viewport_size(), m_device_pixel_ratio, m_is_fullscreen);
client().async_set_maximum_frames_per_second(m_client_state.page_index, m_maximum_frames_per_second);
client().async_set_system_visibility_state(m_client_state.page_index, m_system_visibility_state);
if (Application::browser_options().enable_compositor_process == EnableCompositorProcess::Yes) {
auto compositor_context_id = client().compositor_context_id_for_page(m_client_state.page_index);
Application::the().update_compositor_viewport(compositor_context_id, viewport_size().to_type<int>());
}
client().async_set_document_cookie_version_buffer(m_client_state.page_index, m_document_cookie_version_buffer);
if (auto webdriver_endpoint = Application::browser_options().webdriver_endpoint; webdriver_endpoint.has_value())

View file

@ -101,14 +101,16 @@ WebContentClient::WebContentClient(NonnullOwnPtr<IPC::Transport> transport, View
{
s_clients.set(this);
m_views.set(0, view);
initialize_compositor_connection(*this);
if (Application::browser_options().enable_compositor_process == EnableCompositorProcess::No)
initialize_compositor_connection(*this);
}
WebContentClient::WebContentClient(NonnullOwnPtr<IPC::Transport> transport)
: IPC::ConnectionToServer<WebContentClientEndpoint, WebContentServerEndpoint>(*this, move(transport))
{
s_clients.set(this);
initialize_compositor_connection(*this);
if (Application::browser_options().enable_compositor_process == EnableCompositorProcess::No)
initialize_compositor_connection(*this);
}
WebContentClient::~WebContentClient()
@ -167,11 +169,9 @@ Messages::WebContentClient::AllocateCompositorContextIdResponse WebContentClient
return context_id;
}
void WebContentClient::destroy_compositor_context(Web::Compositor::CompositorContextId context_id)
void WebContentClient::did_destroy_compositor_context(Web::Compositor::CompositorContextId context_id)
{
if (!forget_compositor_context(context_id))
return;
Application::the().destroy_compositor_context(context_id);
forget_compositor_context(context_id);
}
bool WebContentClient::forget_compositor_context(Web::Compositor::CompositorContextId context_id)
@ -249,32 +249,63 @@ void WebContentClient::notify_all_views_of_crash()
bool WebContentClient::send_async_scroll_to_compositor(u64 page_id, Gfx::FloatPoint position, Gfx::FloatPoint delta_in_device_pixels)
{
auto connection = compositor_connections().get(this);
VERIFY(connection.has_value());
VERIFY(connection.value()->is_open());
auto timer = Core::ElapsedTimer::start_new(Core::TimerType::Precise);
auto handled = connection.value()->async_scroll_by(page_id, position, delta_in_device_pixels);
bool handled = false;
if (Application::browser_options().enable_compositor_process == EnableCompositorProcess::Yes) {
handled = Application::the().send_async_scroll_to_compositor(compositor_context_id_for_page(page_id), position, delta_in_device_pixels);
} else {
auto connection = compositor_connections().get(this);
VERIFY(connection.has_value());
VERIFY(connection.value()->is_open());
handled = connection.value()->async_scroll_by(page_id, position, delta_in_device_pixels);
}
dbgln_if(COMPOSITOR_DEBUG, "[Compositor] UI compositor IPC async_scroll_by page {} returned {} in {} us",
page_id, handled, timer.elapsed_time().to_microseconds());
return handled;
}
bool WebContentClient::send_mouse_event_to_compositor(u64 page_id, Web::MouseEvent const& event)
bool WebContentClient::handle_mouse_event_in_compositor(u64 page_id, Web::MouseEvent const& event)
{
auto connection = compositor_connections().get(this);
VERIFY(connection.has_value());
VERIFY(connection.value()->is_open());
auto timer = Core::ElapsedTimer::start_new(Core::TimerType::Precise);
auto handled = connection.value()->mouse_event(page_id, event.clone_without_browser_data());
bool handled = false;
if (Application::browser_options().enable_compositor_process == EnableCompositorProcess::Yes) {
handled = Application::the().handle_mouse_event_in_compositor(compositor_context_id_for_page(page_id), event);
} else {
auto connection = compositor_connections().get(this);
VERIFY(connection.has_value());
VERIFY(connection.value()->is_open());
handled = connection.value()->mouse_event(page_id, event.clone_without_browser_data());
}
dbgln_if(COMPOSITOR_DEBUG, "[Compositor] UI compositor IPC mouse_event page {} returned {} in {} us",
page_id, handled, timer.elapsed_time().to_microseconds());
return handled;
}
void WebContentClient::dispatch_mouse_event_to_web_content(u64 page_id, Web::MouseEvent const& event)
{
if (Application::browser_options().enable_compositor_process == EnableCompositorProcess::Yes) {
Application::the().dispatch_mouse_event_to_web_content(compositor_context_id_for_page(page_id), event);
return;
}
async_mouse_event(page_id, event.clone_without_browser_data());
}
void WebContentClient::notify_presented_bitmap_ready_to_paint(u64 page_id, i32 bitmap_id)
{
if (Application::browser_options().enable_compositor_process == EnableCompositorProcess::Yes) {
auto context_id = m_page_compositor_context_ids.get(page_id);
if (!context_id.has_value())
return;
Application::the().notify_compositor_presented_bitmap_ready_to_paint(*context_id, bitmap_id);
return;
}
VERIFY(try_notify_presented_bitmap_ready_to_paint(*this, page_id, bitmap_id));
}
@ -287,7 +318,10 @@ void WebContentClient::did_present_bitmap(u64 page_id, Gfx::IntRect rect, i32 bi
} else {
dbgln_if(COMPOSITOR_DEBUG, "[Compositor] UI dropping did_paint for page {} bitmap {}: no view",
page_id, bitmap_id);
try_notify_presented_bitmap_ready_to_paint(*this, page_id, bitmap_id);
if (Application::browser_options().enable_compositor_process == EnableCompositorProcess::Yes)
notify_presented_bitmap_ready_to_paint(page_id, bitmap_id);
else
try_notify_presented_bitmap_ready_to_paint(*this, page_id, bitmap_id);
}
}

View file

@ -69,7 +69,8 @@ public:
Web::Compositor::CompositorContextId compositor_context_id_for_page(u64 page_id);
Optional<u64> page_id_for_compositor_context_id(Web::Compositor::CompositorContextId) const;
bool send_async_scroll_to_compositor(u64 page_id, Gfx::FloatPoint position, Gfx::FloatPoint delta_in_device_pixels);
bool send_mouse_event_to_compositor(u64 page_id, Web::MouseEvent const&);
bool handle_mouse_event_in_compositor(u64 page_id, Web::MouseEvent const&);
void dispatch_mouse_event_to_web_content(u64 page_id, Web::MouseEvent const&);
void notify_presented_bitmap_ready_to_paint(u64 page_id, i32 bitmap_id);
void did_present_backing_stores(u64 page_id, i32 front_bitmap_id, Gfx::SharedImage front_backing_store, i32 back_bitmap_id, Gfx::SharedImage back_backing_store);
void did_present_bitmap(u64 page_id, Gfx::IntRect, i32 bitmap_id);
@ -85,7 +86,7 @@ private:
virtual void die() override;
virtual Messages::WebContentClient::AllocateCompositorContextIdResponse allocate_compositor_context_id(u64 page_id, Web::Compositor::PagePresentationRegistration) override;
virtual void destroy_compositor_context(Web::Compositor::CompositorContextId) override;
virtual void did_destroy_compositor_context(Web::Compositor::CompositorContextId) override;
virtual void did_request_new_process_for_navigation(u64 page_id, URL::URL url) override;
virtual void did_finish_loading(u64 page_id, URL::URL) override;
virtual void did_request_refresh(u64 page_id) override;

View file

@ -12,7 +12,6 @@ endpoint CompositorControlServer
connect_web_content() => (IPC::TransportHandle handle, i32 web_content_connection_id)
create_context(Web::Compositor::CompositorContextId context_id, Optional<u64> page_id, Web::Compositor::PagePresentationRegistration page_presentation_registration, i32 web_content_connection_id) => ()
destroy_context(Web::Compositor::CompositorContextId context_id) =|
viewport_size_updated(Web::Compositor::CompositorContextId context_id, Gfx::IntSize viewport_size, bool is_top_level_traversable, Web::Compositor::WindowResizingInProgress window_resize_in_progress) =|

View file

@ -69,11 +69,6 @@ void ConnectionFromClient::create_context(Web::Compositor::CompositorContextId c
m_compositor_state->create_context(context_id, page_id, page_presentation_registration, *connection);
}
void ConnectionFromClient::destroy_context(Web::Compositor::CompositorContextId context_id)
{
m_compositor_state->destroy_context(context_id);
}
void ConnectionFromClient::viewport_size_updated(Web::Compositor::CompositorContextId context_id, Gfx::IntSize viewport_size, bool is_top_level_traversable, Web::Compositor::WindowResizingInProgress window_resize_in_progress)
{
m_compositor_state->viewport_size_updated(context_id, viewport_size, is_top_level_traversable, window_resize_in_progress);

View file

@ -38,7 +38,6 @@ private:
virtual Messages::CompositorControlServer::InitTransportResponse init_transport(int peer_pid) override;
virtual Messages::CompositorControlServer::ConnectWebContentResponse connect_web_content() override;
virtual void create_context(Web::Compositor::CompositorContextId, Optional<u64> page_id, Web::Compositor::PagePresentationRegistration, i32 web_content_connection_id) override;
virtual void destroy_context(Web::Compositor::CompositorContextId) override;
virtual void viewport_size_updated(Web::Compositor::CompositorContextId, Gfx::IntSize, bool is_top_level_traversable, Web::Compositor::WindowResizingInProgress) override;
virtual Messages::CompositorControlServer::HandleMouseEventResponse handle_mouse_event(Web::Compositor::CompositorContextId, Web::MouseEvent) override;
virtual void dispatch_mouse_event_to_web_content(Web::Compositor::CompositorContextId, Web::MouseEvent) override;

View file

@ -66,6 +66,7 @@
#include <WebContent/PageClient.h>
#include <WebContent/PageHost.h>
#include <WebContent/WebContentClientEndpoint.h>
#include <WebContent/WebContentCompositorHost.h>
namespace WebContent {
@ -77,6 +78,18 @@ ConnectionFromClient::ConnectionFromClient(NonnullOwnPtr<IPC::Transport> transpo
ConnectionFromClient::~ConnectionFromClient() = default;
CompositorConnection& ConnectionFromClient::compositor_process_connection() const
{
VERIFY(m_compositor_connection);
VERIFY(m_compositor_connection->is_open());
return *m_compositor_connection;
}
void ConnectionFromClient::did_destroy_compositor_context(Web::Compositor::CompositorContextId context_id)
{
async_did_destroy_compositor_context(context_id);
}
void ConnectionFromClient::die()
{
_exit(0);
@ -158,7 +171,11 @@ void ConnectionFromClient::connect_to_compositor(IPC::TransportHandle handle)
void ConnectionFromClient::connect_to_compositor_process(IPC::TransportHandle handle)
{
(void)handle;
auto transport = MUST(handle.create_transport());
m_compositor_connection = adopt_ref(*new CompositorConnection(move(transport)));
m_compositor_connection->on_mouse_event = [this](u64 page_id, Web::MouseEvent event) {
mouse_event(page_id, move(event));
};
}
void ConnectionFromClient::connect_to_request_server(IPC::TransportHandle handle)

View file

@ -19,6 +19,7 @@
#include <LibWeb/CSS/PreferredColorScheme.h>
#include <LibWeb/CSS/PreferredContrast.h>
#include <LibWeb/CSS/PreferredMotion.h>
#include <LibWeb/Compositor/Types.h>
#include <LibWeb/Forward.h>
#include <LibWeb/Loader/FileRequest.h>
#include <LibWeb/Page/EventResult.h>
@ -48,6 +49,8 @@ public:
PageHost& page_host() { return *m_page_host; }
PageHost const& page_host() const { return *m_page_host; }
CompositorConnection& compositor_process_connection() const;
void did_destroy_compositor_context(Web::Compositor::CompositorContextId);
Function<void(IPC::TransportHandle const&)> on_request_server_connection;
Function<void(IPC::TransportHandle const&)> on_image_decoder_connection;
@ -179,6 +182,7 @@ private:
virtual void exit_fullscreen(u64 page_id) override;
RefPtr<CompositorConnection> m_compositor_connection;
NonnullOwnPtr<PageHost> m_page_host;
HashMap<int, Web::FileRequest> m_requested_files {};
@ -187,7 +191,6 @@ private:
void enqueue_input_event(Web::QueuedInputEvent);
Queue<Web::QueuedInputEvent> m_input_event_queue;
RefPtr<CompositorConnection> m_compositor_connection;
};
}

View file

@ -50,7 +50,7 @@ void PageHost::ensure_compositor_host(Web::DisplayListPlayerType display_list_pl
{
if (m_compositor_host)
return;
m_compositor_host = create_web_content_compositor_host();
m_compositor_host = create_web_content_compositor_host(m_client);
m_compositor_host->start(display_list_player_type);
}

View file

@ -35,7 +35,7 @@
endpoint WebContentClient
{
allocate_compositor_context_id(u64 page_id, Web::Compositor::PagePresentationRegistration page_presentation_registration) => (Web::Compositor::CompositorContextId context_id)
destroy_compositor_context(Web::Compositor::CompositorContextId context_id) =|
did_destroy_compositor_context(Web::Compositor::CompositorContextId context_id) =|
did_request_new_process_for_navigation(u64 page_id, URL::URL url) =|
did_start_loading(u64 page_id, URL::URL url, bool is_redirect) =|

View file

@ -5,6 +5,7 @@
*/
#include <AK/Debug.h>
#include <AK/HashMap.h>
#include <LibCore/EventLoop.h>
#include <LibCore/Timer.h>
#include <LibGfx/Bitmap.h>
@ -20,13 +21,26 @@
#include <LibWeb/Painting/DisplayList.h>
#include <LibWeb/Painting/DisplayListResourceStorage.h>
#include <WebContent/CompositorClientEndpoint.h>
#include <WebContent/CompositorConnection.h>
#include <WebContent/CompositorServerEndpoint.h>
#include <WebContent/ConnectionFromClient.h>
#include <WebContent/WebContentCompositorClientEndpoint.h>
#include <WebContent/WebContentCompositorHost.h>
#include <WebContent/WebContentCompositorServerEndpoint.h>
namespace WebContent {
static bool& should_use_compositor_process()
{
static bool flag = false;
return flag;
}
void set_should_use_compositor_process(bool enabled)
{
should_use_compositor_process() = enabled;
}
class WebContentCompositorActor;
class WebContentCompositorConnectionToServer final
@ -388,7 +402,7 @@ void CompositorConnectionFromClient::ready_to_paint(u64 page_id, i32 bitmap_id)
m_actor.presented_bitmap_ready_to_paint(page_id, bitmap_id);
}
class WebContentCompositorHost final : public Web::Compositor::CompositorHost {
class LocalWebContentCompositorHost final : public Web::Compositor::CompositorHost {
public:
virtual void start(Web::DisplayListPlayerType display_list_player_type) override
{
@ -525,9 +539,128 @@ private:
RefPtr<Threading::Thread> m_actor_thread;
};
NonnullOwnPtr<Web::Compositor::CompositorHost> create_web_content_compositor_host()
class ProcessWebContentCompositorHost final : public Web::Compositor::CompositorHost {
public:
explicit ProcessWebContentCompositorHost(ConnectionFromClient& client)
: m_client(client)
{
}
virtual void start(Web::DisplayListPlayerType) override
{
}
private:
virtual void register_context(Web::Compositor::CompositorContextId context_id, Optional<u64> page_id, Web::Compositor::PagePresentationRegistration page_presentation_registration) override
{
if (page_presentation_registration == Web::Compositor::PagePresentationRegistration::Yes) {
VERIFY(page_id.has_value());
} else {
VERIFY(!page_id.has_value());
VERIFY(!Web::Compositor::is_page_presenting_compositor_context_id(context_id));
}
}
virtual void destroy_context(Web::Compositor::CompositorContextId context_id) override
{
connection().destroy_context(context_id);
m_client.did_destroy_compositor_context(context_id);
}
virtual void stop_presenting_to_client(Web::Compositor::CompositorContextId context_id) override
{
connection().stop_presenting_to_client(context_id);
}
virtual void set_presentation_mode(Web::Compositor::CompositorContextId context_id, Web::Compositor::PresentationMode mode) override
{
connection().set_presentation_mode(context_id, mode);
}
virtual void update_display_list(Web::Compositor::CompositorContextId context_id, NonnullRefPtr<Web::Painting::DisplayList> display_list, Web::Painting::DisplayListResourceTransaction&& resource_transaction, Web::Painting::ScrollStateSnapshot&& scroll_state_snapshot) override
{
connection().update_display_list(context_id, display_list, resource_transaction, scroll_state_snapshot);
}
virtual void update_video_frame(Web::Compositor::CompositorContextId context_id, Web::Painting::VideoFrameResourceId frame_id, NonnullRefPtr<Media::VideoFrame const> frame) override
{
connection().update_video_frame(context_id, frame_id, frame);
}
virtual void clear_video_frame(Web::Compositor::CompositorContextId context_id, Web::Painting::VideoFrameResourceId frame_id) override
{
connection().clear_video_frame(context_id, frame_id);
}
virtual void update_compositor_surface(Web::Compositor::CompositorContextId context_id, Web::Painting::CompositorSurfaceId surface_id, Gfx::SharedImage&& shared_image) override
{
connection().update_compositor_surface(context_id, surface_id, shared_image);
}
virtual void clear_compositor_surface(Web::Compositor::CompositorContextId context_id, Web::Painting::CompositorSurfaceId surface_id) override
{
connection().clear_compositor_surface(context_id, surface_id);
}
virtual void update_scroll_state(Web::Compositor::CompositorContextId context_id, Web::Painting::ScrollStateSnapshot&& scroll_state_snapshot) override
{
connection().update_scroll_state(context_id, scroll_state_snapshot);
}
virtual void invalidate_wheel_event_listener_state(Web::Compositor::CompositorContextId context_id, u64 generation) override
{
connection().invalidate_wheel_event_listener_state(context_id, generation);
}
virtual Web::Compositor::AsyncScrollEnqueueResult async_scroll_by(Web::Compositor::CompositorContextId context_id, Web::UniqueNodeID expected_document_id, Gfx::FloatPoint position,
Gfx::FloatPoint delta_in_device_pixels, Gfx::IntRect viewport_rect, Web::Compositor::AsyncScrollOperationTracking operation_tracking) override
{
return connection().async_scroll_by(context_id, expected_document_id, position, delta_in_device_pixels, viewport_rect, operation_tracking);
}
virtual bool should_defer_async_scroll_offset_adoption(Web::Compositor::CompositorContextId) const override
{
return false;
}
virtual bool should_defer_main_thread_present_for_async_scroll(Web::Compositor::CompositorContextId context_id) const override
{
return connection().should_defer_main_thread_present_for_async_scroll(context_id);
}
virtual Web::Compositor::PendingAsyncScrollUpdates take_pending_async_scroll_updates(Web::Compositor::CompositorContextId context_id) override
{
return connection().take_pending_async_scroll_updates(context_id);
}
virtual void viewport_size_updated(Web::Compositor::CompositorContextId context_id, Gfx::IntSize viewport_size, bool is_top_level_traversable, Web::Compositor::WindowResizingInProgress window_resize_in_progress) override
{
connection().viewport_size_updated(context_id, viewport_size, is_top_level_traversable, window_resize_in_progress);
}
virtual void present_frame(Web::Compositor::CompositorContextId context_id, Gfx::IntRect viewport_rect) override
{
connection().present_frame(context_id, viewport_rect);
}
virtual void request_screenshot(Web::Compositor::CompositorContextId context_id, NonnullRefPtr<Gfx::PaintingSurface> target_surface, Function<void()>&& callback) override
{
connection().request_screenshot(context_id, move(target_surface), move(callback));
}
CompositorConnection& connection() const
{
return m_client.compositor_process_connection();
}
ConnectionFromClient& m_client;
};
NonnullOwnPtr<Web::Compositor::CompositorHost> create_web_content_compositor_host(ConnectionFromClient& client)
{
return make<WebContentCompositorHost>();
if (should_use_compositor_process())
return make<ProcessWebContentCompositorHost>(client);
return make<LocalWebContentCompositorHost>();
}
}

View file

@ -6,11 +6,15 @@
#pragma once
#include <AK/Function.h>
#include <AK/NonnullOwnPtr.h>
#include <AK/RefPtr.h>
#include <LibWeb/Compositor/CompositorHost.h>
#include <WebContent/Forward.h>
namespace WebContent {
NonnullOwnPtr<Web::Compositor::CompositorHost> create_web_content_compositor_host();
NonnullOwnPtr<Web::Compositor::CompositorHost> create_web_content_compositor_host(ConnectionFromClient&);
void set_should_use_compositor_process(bool);
}

View file

@ -39,6 +39,7 @@
#include <LibWebView/Utilities.h>
#include <WebContent/ConnectionFromClient.h>
#include <WebContent/PageClient.h>
#include <WebContent/WebContentCompositorHost.h>
#include <WebContent/WebDriverConnection.h>
#include <openssl/thread.h>
@ -149,6 +150,7 @@ ErrorOr<int> ladybird_main(Main::Arguments arguments)
bool is_headless = false;
bool disable_scrollbar_painting = false;
bool disable_async_scrolling = false;
bool enable_compositor_process = false;
StringView echo_server_port_string_view {};
StringView default_time_zone {};
StringView style_invalidation_counter_dump_interval {};
@ -171,6 +173,7 @@ ErrorOr<int> ladybird_main(Main::Arguments arguments)
args_parser.add_option(collect_garbage_on_every_allocation, "Collect garbage after every JS heap allocation", "collect-garbage-on-every-allocation");
args_parser.add_option(disable_scrollbar_painting, "Don't paint horizontal or vertical viewport scrollbars", "disable-scrollbar-painting");
args_parser.add_option(disable_async_scrolling, "Disable async scrolling", "disable-async-scrolling");
args_parser.add_option(enable_compositor_process, "Use the Compositor helper process", "enable-compositor-process");
args_parser.add_option(echo_server_port_string_view, "Echo server port used in test internals", "echo-server-port", 0, "echo_server_port");
args_parser.add_option(is_headless, "Report that the browser is running in headless mode", "headless");
args_parser.add_option(default_time_zone, "Default time zone", "default-time-zone", 0, "time-zone-id");
@ -222,6 +225,7 @@ ErrorOr<int> ladybird_main(Main::Arguments arguments)
Web::Painting::set_paint_viewport_scrollbars(!disable_scrollbar_painting);
WebContent::PageClient::set_async_scrolling_enabled(!disable_async_scrolling);
WebContent::set_should_use_compositor_process(enable_compositor_process);
if (!echo_server_port_string_view.is_empty()) {
if (auto maybe_echo_server_port = echo_server_port_string_view.to_number<u16>(); maybe_echo_server_port.has_value())