Compositor+WebContent+LibWebView: Implement service-side compositor IPC

With CompositorState in place, Browser and WebContent need an actual
wire protocol to drive it: which calls are sync, who validates that a
WebContent is allowed to touch a given context, how compositor loss
propagates back to the renderer. This commit fills in the four endpoints
around CompositorState and the matching WebContent-side helpers so the
future remote host can forward CompositorHost calls directly. The
service still has no clients, so default rendering is unchanged.
This commit is contained in:
Aliaksandr Kalenik 2026-05-22 14:00:41 +02:00 committed by Alexander Kalenik
parent 723e5dde32
commit 17f5865d36
16 changed files with 551 additions and 42 deletions

View file

@ -13,6 +13,7 @@
#include <LibGfx/Point.h>
#include <LibGfx/Rect.h>
#include <LibWeb/Compositor/AsyncScrollingState.h>
#include <LibWeb/Export.h>
#include <LibWeb/Forward.h>
#include <LibWeb/Painting/ScrollState.h>
@ -47,7 +48,7 @@ struct CachedBlockingWheelEventTarget {
// Mutable compositor-side copy of AsyncScrollingState. Current scroll offsets live in ScrollStateSnapshot; this tree
// owns scroll node geometry and derived hit-test targets.
class AsyncScrollTree {
class WEB_API AsyncScrollTree {
public:
void set_state(AsyncScrollingState&&);

View file

@ -14,6 +14,7 @@
#include <LibGfx/CornerRadii.h>
#include <LibGfx/Point.h>
#include <LibGfx/Rect.h>
#include <LibWeb/Export.h>
#include <LibWeb/Forward.h>
#include <LibWeb/Painting/AccumulatedVisualContext.h>
#include <LibWeb/Painting/ScrollFrame.h>
@ -154,13 +155,13 @@ enum class WheelScrollAdmission {
BlockedByWheelEventRegion,
};
void initialize_async_scrolling_metadata_recording(DisplayListRecordingContext&, Painting::ViewportPaintable&);
void record_async_scrolling_metadata_for_paintable(Painting::PaintableBox const&, DisplayListRecordingContext&);
void finalize_async_scrolling_metadata_recording(DisplayListRecordingContext&, HTML::Navigable&, Gfx::IntRect viewport_rect);
AsyncScrollingState async_scrolling_state_from_display_list(Painting::DisplayList const&);
WheelRoutingAdmission wheel_routing_admission_for(AsyncScrollingState const&);
StringView wheel_routing_admission_to_string(WheelRoutingAdmission);
bool blocks_wheel_event_at_position(AsyncScrollingState const&, RefPtr<Painting::DisplayList> const&, Painting::ScrollStateSnapshot const&, Gfx::FloatPoint position);
WheelScrollAdmission admit_wheel_scroll(AsyncScrollingState const&, RefPtr<Painting::DisplayList> const&, Painting::ScrollStateSnapshot const&, Gfx::FloatPoint position, Gfx::FloatPoint delta, bool blocking_wheel_event_regions_are_current);
WEB_API void initialize_async_scrolling_metadata_recording(DisplayListRecordingContext&, Painting::ViewportPaintable&);
WEB_API void record_async_scrolling_metadata_for_paintable(Painting::PaintableBox const&, DisplayListRecordingContext&);
WEB_API void finalize_async_scrolling_metadata_recording(DisplayListRecordingContext&, HTML::Navigable&, Gfx::IntRect viewport_rect);
WEB_API AsyncScrollingState async_scrolling_state_from_display_list(Painting::DisplayList const&);
WEB_API WheelRoutingAdmission wheel_routing_admission_for(AsyncScrollingState const&);
WEB_API StringView wheel_routing_admission_to_string(WheelRoutingAdmission);
WEB_API bool blocks_wheel_event_at_position(AsyncScrollingState const&, RefPtr<Painting::DisplayList> const&, Painting::ScrollStateSnapshot const&, Gfx::FloatPoint position);
WEB_API WheelScrollAdmission admit_wheel_scroll(AsyncScrollingState const&, RefPtr<Painting::DisplayList> const&, Painting::ScrollStateSnapshot const&, Gfx::FloatPoint position, Gfx::FloatPoint delta, bool blocking_wheel_event_regions_are_current);
}

View file

@ -24,7 +24,11 @@ void CompositorClient::die()
}
}
void CompositorClient::did_connect_web_content(i32)
void CompositorClient::did_allocate_backing_stores(Web::Compositor::CompositorContextId, i32, Gfx::SharedImage, i32, Gfx::SharedImage)
{
}
void CompositorClient::did_present_frame(Web::Compositor::CompositorContextId, Gfx::IntRect, i32)
{
}

View file

@ -9,7 +9,10 @@
#include <AK/Function.h>
#include <Compositor/CompositorControlClientEndpoint.h>
#include <Compositor/CompositorControlServerEndpoint.h>
#include <LibGfx/Rect.h>
#include <LibGfx/SharedImage.h>
#include <LibIPC/ConnectionToServer.h>
#include <LibWeb/Compositor/Types.h>
#include <LibWebView/Forward.h>
namespace WebView {
@ -29,7 +32,8 @@ public:
private:
virtual void die() override;
virtual void did_connect_web_content(i32 web_content_connection_id) override;
virtual void did_allocate_backing_stores(Web::Compositor::CompositorContextId, i32 front_bitmap_id, Gfx::SharedImage front_backing_store, i32 back_bitmap_id, Gfx::SharedImage back_backing_store) override;
virtual void did_present_frame(Web::Compositor::CompositorContextId, Gfx::IntRect content_rect, i32 bitmap_id) override;
};
}

View file

@ -1,4 +1,9 @@
#include <LibGfx/Rect.h>
#include <LibGfx/SharedImage.h>
#include <LibWeb/Compositor/Types.h>
endpoint CompositorControlClient
{
did_connect_web_content(i32 web_content_connection_id) =|
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) =|
did_present_frame(Web::Compositor::CompositorContextId context_id, Gfx::IntRect content_rect, i32 bitmap_id) =|
}

View file

@ -1,8 +1,23 @@
#include <AK/Optional.h>
#include <LibGfx/Point.h>
#include <LibGfx/Size.h>
#include <LibIPC/TransportHandle.h>
#include <LibWeb/Compositor/Types.h>
#include <LibWeb/Page/InputEvent.h>
endpoint CompositorControlServer
{
init_transport(int peer_pid) => (int peer_pid)
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) =|
handle_mouse_event(Web::Compositor::CompositorContextId context_id, Web::MouseEvent event) => (bool handled)
dispatch_mouse_event_to_web_content(Web::Compositor::CompositorContextId context_id, Web::MouseEvent event) =|
async_scroll_by(Web::Compositor::CompositorContextId context_id, Gfx::FloatPoint position, Gfx::FloatPoint delta_in_device_pixels) => (bool handled)
presented_bitmap_ready_to_paint(Web::Compositor::CompositorContextId context_id, i32 bitmap_id) =|
}

View file

@ -1,4 +1,11 @@
#include <LibWeb/Compositor/Types.h>
#include <LibWeb/Page/InputEvent.h>
endpoint CompositorWebContentClient
{
did_connect() =|
mouse_event(u64 page_id, Web::MouseEvent event) =|
request_rendering_update() =|
did_complete_screenshot(Web::Compositor::ScreenshotRequestId request_id) =|
did_fail_screenshot(Web::Compositor::ScreenshotRequestId request_id) =|
did_lose_compositor() =|
}

View file

@ -1,4 +1,36 @@
#include <AK/NonnullRefPtr.h>
#include <LibGfx/Point.h>
#include <LibGfx/Rect.h>
#include <LibGfx/SharedImage.h>
#include <LibGfx/Size.h>
#include <LibMedia/VideoFrame.h>
#include <LibWeb/Compositor/Types.h>
#include <LibWeb/Forward.h>
#include <LibWeb/Painting/DisplayList.h>
#include <LibWeb/Painting/DisplayListResourceStorage.h>
#include <LibWeb/Painting/ScrollState.h>
endpoint CompositorWebContentServer
{
ping() =|
set_presentation_mode(Web::Compositor::CompositorContextId context_id, Web::Compositor::PresentationMode presentation_mode) =|
stop_presenting_to_client(Web::Compositor::CompositorContextId context_id) =|
destroy_context(Web::Compositor::CompositorContextId context_id) =|
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) =|
update_scroll_state(Web::Compositor::CompositorContextId context_id, Web::Painting::ScrollStateSnapshot scroll_state_snapshot) =|
update_video_frame(Web::Compositor::CompositorContextId context_id, Web::Painting::VideoFrameResourceId frame_id, NonnullRefPtr<Media::VideoFrame const> frame) =|
clear_video_frame(Web::Compositor::CompositorContextId context_id, Web::Painting::VideoFrameResourceId frame_id) =|
update_compositor_surface(Web::Compositor::CompositorContextId context_id, Web::Painting::CompositorSurfaceId surface_id, Gfx::SharedImage shared_image) =|
clear_compositor_surface(Web::Compositor::CompositorContextId context_id, Web::Painting::CompositorSurfaceId surface_id) =|
invalidate_wheel_event_listener_state(Web::Compositor::CompositorContextId context_id, u64 generation) =|
async_scroll_by(Web::Compositor::CompositorContextId context_id, Web::UniqueNodeID document_id, Gfx::FloatPoint position, Gfx::FloatPoint delta, Gfx::IntRect viewport_rect, Web::Compositor::AsyncScrollOperationTracking operation_tracking) => (Web::Compositor::AsyncScrollEnqueueResult result)
should_defer_main_thread_present_for_async_scroll(Web::Compositor::CompositorContextId context_id) => (bool should_defer)
take_pending_async_scroll_updates(Web::Compositor::CompositorContextId context_id) => (Web::Compositor::PendingAsyncScrollUpdates updates)
viewport_size_updated(Web::Compositor::CompositorContextId context_id, Gfx::IntSize viewport_size, bool is_top_level_traversable, Web::Compositor::WindowResizingInProgress window_resize_in_progress) =|
present_frame(Web::Compositor::CompositorContextId context_id, Gfx::IntRect viewport_rect) =|
request_screenshot(Web::Compositor::CompositorContextId context_id, Web::Compositor::ScreenshotRequestId request_id, Gfx::ShareableBitmap target_bitmap) =|
}

View file

@ -4,6 +4,7 @@
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/IDAllocator.h>
#include <Compositor/ConnectionFromClient.h>
#include <Compositor/ConnectionFromWebContent.h>
#include <LibCore/EventLoop.h>
@ -12,16 +13,32 @@
namespace Compositor {
ConnectionFromClient::ConnectionFromClient(NonnullOwnPtr<IPC::Transport> transport)
static IDAllocator s_web_content_connection_ids;
ConnectionFromClient::ConnectionFromClient(NonnullOwnPtr<IPC::Transport> transport, RefPtr<Gfx::SkiaBackendContext> skia_backend_context, bool async_scrolling_enabled)
: IPC::ConnectionFromClient<CompositorControlClientEndpoint, CompositorControlServerEndpoint>(*this, move(transport), 1)
, m_compositor_state(CompositorState::create(move(skia_backend_context), async_scrolling_enabled))
{
m_compositor_state->set_client(*this);
}
void ConnectionFromClient::die()
{
for (auto& [id, connection] : m_web_content_connections)
connection->notify_compositor_lost();
Core::EventLoop::current().quit(0);
}
void ConnectionFromClient::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)
{
async_did_allocate_backing_stores(context_id, front_bitmap_id, move(front_backing_store), back_bitmap_id, move(back_backing_store));
}
void ConnectionFromClient::did_present_frame(Web::Compositor::CompositorContextId context_id, Gfx::IntRect content_rect, i32 bitmap_id)
{
async_did_present_frame(context_id, content_rect, bitmap_id);
}
Messages::CompositorControlServer::InitTransportResponse ConnectionFromClient::init_transport([[maybe_unused]] int peer_pid)
{
#ifdef AK_OS_WINDOWS
@ -34,14 +51,68 @@ Messages::CompositorControlServer::InitTransportResponse ConnectionFromClient::i
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());
};
auto web_content_connection_id = s_web_content_connection_ids.allocate();
auto connection = ConnectionFromWebContent::construct(move(paired_transport.local), m_compositor_state, web_content_connection_id);
connection->set_on_death([this](ConnectionFromWebContent& dead) {
auto client_id = dead.client_id();
m_web_content_connections.remove(client_id);
s_web_content_connection_ids.deallocate(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 };
}
void ConnectionFromClient::create_context(Web::Compositor::CompositorContextId context_id, Optional<u64> page_id, Web::Compositor::PagePresentationRegistration page_presentation_registration, i32 web_content_connection_id)
{
auto* connection = web_content_connection(web_content_connection_id);
VERIFY(connection);
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);
}
Messages::CompositorControlServer::HandleMouseEventResponse ConnectionFromClient::handle_mouse_event(Web::Compositor::CompositorContextId context_id, Web::MouseEvent event)
{
return m_compositor_state->handle_mouse_event(context_id, event);
}
void ConnectionFromClient::dispatch_mouse_event_to_web_content(Web::Compositor::CompositorContextId context_id, Web::MouseEvent event)
{
m_compositor_state->dispatch_mouse_event_to_web_content(context_id, event);
}
Messages::CompositorControlServer::AsyncScrollByResponse ConnectionFromClient::async_scroll_by(Web::Compositor::CompositorContextId context_id, Gfx::FloatPoint position, Gfx::FloatPoint delta_in_device_pixels)
{
auto handled = m_compositor_state->async_scroll_by(context_id, position, delta_in_device_pixels);
if (handled) {
deferred_invoke([this, context_id] {
if (!is_open())
return;
m_compositor_state->present_deferred_async_scroll_frame(context_id);
});
}
return handled;
}
void ConnectionFromClient::presented_bitmap_ready_to_paint(Web::Compositor::CompositorContextId context_id, i32 bitmap_id)
{
m_compositor_state->presented_bitmap_ready_to_paint(context_id, bitmap_id);
}
ConnectionFromWebContent* ConnectionFromClient::web_content_connection(i32 web_content_connection_id)
{
auto it = m_web_content_connections.find(web_content_connection_id);
if (it == m_web_content_connections.end())
return nullptr;
return it->value.ptr();
}
}

View file

@ -10,26 +10,45 @@
#include <AK/NonnullRefPtr.h>
#include <Compositor/CompositorControlClientEndpoint.h>
#include <Compositor/CompositorControlServerEndpoint.h>
#include <Compositor/CompositorState.h>
#include <Compositor/Forward.h>
#include <LibIPC/ConnectionFromClient.h>
#include <LibIPC/TransportHandle.h>
#include <LibWeb/Painting/DisplayList.h>
#include <LibWeb/Painting/DisplayListResourceStorage.h>
namespace Compositor {
class ConnectionFromClient final
: public IPC::ConnectionFromClient<CompositorControlClientEndpoint, CompositorControlServerEndpoint> {
C_OBJECT(ConnectionFromClient)
: public IPC::ConnectionFromClient<CompositorControlClientEndpoint, CompositorControlServerEndpoint>
, public CompositorStateClient {
C_OBJECT(ConnectionFromClient);
public:
virtual void die() override;
virtual ~ConnectionFromClient() override = default;
private:
explicit ConnectionFromClient(NonnullOwnPtr<IPC::Transport>);
ConnectionFromClient(NonnullOwnPtr<IPC::Transport>, RefPtr<Gfx::SkiaBackendContext>, bool async_scrolling_enabled);
virtual void die() override;
virtual void did_allocate_backing_stores(Web::Compositor::CompositorContextId, i32 front_bitmap_id, Gfx::SharedImage&& front_backing_store, i32 back_bitmap_id, Gfx::SharedImage&& back_backing_store) override;
virtual void did_present_frame(Web::Compositor::CompositorContextId, Gfx::IntRect content_rect, i32 bitmap_id) override;
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;
virtual Messages::CompositorControlServer::AsyncScrollByResponse async_scroll_by(Web::Compositor::CompositorContextId, Gfx::FloatPoint position, Gfx::FloatPoint delta_in_device_pixels) override;
virtual void presented_bitmap_ready_to_paint(Web::Compositor::CompositorContextId, i32 bitmap_id) override;
ConnectionFromWebContent* web_content_connection(i32 web_content_connection_id);
i32 m_next_web_content_connection_id { 1 };
HashMap<i32, NonnullRefPtr<ConnectionFromWebContent>> m_web_content_connections;
NonnullRefPtr<CompositorState> m_compositor_state;
};
}

View file

@ -5,22 +5,154 @@
*/
#include <Compositor/ConnectionFromWebContent.h>
#include <LibWeb/Page/InputEvent.h>
namespace Compositor {
ConnectionFromWebContent::ConnectionFromWebContent(NonnullOwnPtr<IPC::Transport> transport, int client_id)
ConnectionFromWebContent::ConnectionFromWebContent(NonnullOwnPtr<IPC::Transport> transport, NonnullRefPtr<CompositorState> compositor_state, int client_id)
: IPC::ConnectionFromClient<CompositorWebContentClientEndpoint, CompositorWebContentServerEndpoint>(*this, move(transport), client_id)
, m_compositor_state(move(compositor_state))
{
}
void ConnectionFromWebContent::die()
{
if (on_death)
on_death(*this);
auto protector = NonnullRefPtr { *this };
m_compositor_state->destroy_contexts_for_web_content_client(*this);
if (m_on_death)
m_on_death(*this);
}
void ConnectionFromWebContent::ping()
void ConnectionFromWebContent::notify_compositor_lost()
{
async_did_lose_compositor();
}
void ConnectionFromWebContent::request_rendering_update()
{
async_request_rendering_update();
}
void ConnectionFromWebContent::dispatch_mouse_event_to_web_content(u64 page_id, Web::MouseEvent const& event)
{
async_mouse_event(page_id, event);
}
void ConnectionFromWebContent::verify_context_is_owned_by_this_connection(Web::Compositor::CompositorContextId context_id)
{
switch (m_compositor_state->check_context_owner(context_id, *this)) {
case CompositorState::ContextOwnerCheckResult::OwnedByClient:
return;
case CompositorState::ContextOwnerCheckResult::ContextUnavailable:
VERIFY_NOT_REACHED();
case CompositorState::ContextOwnerCheckResult::ConflictingOwner:
did_misbehave("WebContent tried to use a compositor context owned by another connection");
VERIFY_NOT_REACHED();
}
VERIFY_NOT_REACHED();
}
void ConnectionFromWebContent::set_presentation_mode(Web::Compositor::CompositorContextId context_id, Web::Compositor::PresentationMode presentation_mode)
{
verify_context_is_owned_by_this_connection(context_id);
m_compositor_state->set_presentation_mode(context_id, move(presentation_mode));
}
void ConnectionFromWebContent::stop_presenting_to_client(Web::Compositor::CompositorContextId context_id)
{
verify_context_is_owned_by_this_connection(context_id);
m_compositor_state->stop_presenting_to_client(context_id);
}
void ConnectionFromWebContent::destroy_context(Web::Compositor::CompositorContextId context_id)
{
verify_context_is_owned_by_this_connection(context_id);
m_compositor_state->destroy_context(context_id);
}
void ConnectionFromWebContent::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)
{
verify_context_is_owned_by_this_connection(context_id);
m_compositor_state->update_display_list(context_id, move(display_list), move(resource_transaction), move(scroll_state_snapshot));
}
void ConnectionFromWebContent::update_scroll_state(Web::Compositor::CompositorContextId context_id, Web::Painting::ScrollStateSnapshot scroll_state_snapshot)
{
verify_context_is_owned_by_this_connection(context_id);
m_compositor_state->update_scroll_state(context_id, move(scroll_state_snapshot));
}
void ConnectionFromWebContent::update_video_frame(Web::Compositor::CompositorContextId context_id, Web::Painting::VideoFrameResourceId frame_id, NonnullRefPtr<Media::VideoFrame const> frame)
{
verify_context_is_owned_by_this_connection(context_id);
m_compositor_state->update_video_frame(context_id, frame_id, move(frame));
}
void ConnectionFromWebContent::clear_video_frame(Web::Compositor::CompositorContextId context_id, Web::Painting::VideoFrameResourceId frame_id)
{
verify_context_is_owned_by_this_connection(context_id);
m_compositor_state->clear_video_frame(context_id, frame_id);
}
void ConnectionFromWebContent::update_compositor_surface(Web::Compositor::CompositorContextId context_id, Web::Painting::CompositorSurfaceId surface_id, Gfx::SharedImage shared_image)
{
verify_context_is_owned_by_this_connection(context_id);
m_compositor_state->update_compositor_surface(context_id, surface_id, move(shared_image));
}
void ConnectionFromWebContent::clear_compositor_surface(Web::Compositor::CompositorContextId context_id, Web::Painting::CompositorSurfaceId surface_id)
{
verify_context_is_owned_by_this_connection(context_id);
m_compositor_state->clear_compositor_surface(context_id, surface_id);
}
void ConnectionFromWebContent::invalidate_wheel_event_listener_state(Web::Compositor::CompositorContextId context_id, u64 generation)
{
verify_context_is_owned_by_this_connection(context_id);
m_compositor_state->invalidate_wheel_event_listener_state(context_id, generation);
}
Messages::CompositorWebContentServer::AsyncScrollByResponse ConnectionFromWebContent::async_scroll_by(Web::Compositor::CompositorContextId context_id, Web::UniqueNodeID document_id, Gfx::FloatPoint position, Gfx::FloatPoint delta, Gfx::IntRect viewport_rect, Web::Compositor::AsyncScrollOperationTracking operation_tracking)
{
verify_context_is_owned_by_this_connection(context_id);
auto result = m_compositor_state->async_scroll_by(context_id, document_id, position, delta, viewport_rect, operation_tracking);
if (result.accepted)
async_request_rendering_update();
return result;
}
Messages::CompositorWebContentServer::ShouldDeferMainThreadPresentForAsyncScrollResponse ConnectionFromWebContent::should_defer_main_thread_present_for_async_scroll(Web::Compositor::CompositorContextId context_id)
{
verify_context_is_owned_by_this_connection(context_id);
return m_compositor_state->should_defer_main_thread_present_for_async_scroll(context_id);
}
Messages::CompositorWebContentServer::TakePendingAsyncScrollUpdatesResponse ConnectionFromWebContent::take_pending_async_scroll_updates(Web::Compositor::CompositorContextId context_id)
{
verify_context_is_owned_by_this_connection(context_id);
return m_compositor_state->take_pending_async_scroll_updates(context_id);
}
void ConnectionFromWebContent::viewport_size_updated(Web::Compositor::CompositorContextId context_id, Gfx::IntSize viewport_size, bool is_top_level_traversable, Web::Compositor::WindowResizingInProgress window_resize_in_progress)
{
verify_context_is_owned_by_this_connection(context_id);
m_compositor_state->viewport_size_updated(context_id, viewport_size, is_top_level_traversable, window_resize_in_progress);
}
void ConnectionFromWebContent::present_frame(Web::Compositor::CompositorContextId context_id, Gfx::IntRect viewport_rect)
{
verify_context_is_owned_by_this_connection(context_id);
m_compositor_state->present_frame(context_id, viewport_rect);
}
void ConnectionFromWebContent::request_screenshot(Web::Compositor::CompositorContextId context_id, Web::Compositor::ScreenshotRequestId request_id, Gfx::ShareableBitmap target_bitmap)
{
verify_context_is_owned_by_this_connection(context_id);
if (m_compositor_state->request_screenshot(context_id, target_bitmap))
async_did_complete_screenshot(request_id);
else
async_did_fail_screenshot(request_id);
}
}

View file

@ -7,25 +7,53 @@
#pragma once
#include <AK/Function.h>
#include <Compositor/CompositorState.h>
#include <Compositor/CompositorWebContentClientEndpoint.h>
#include <Compositor/CompositorWebContentServerEndpoint.h>
#include <LibIPC/ConnectionFromClient.h>
#include <LibWeb/Painting/DisplayList.h>
#include <LibWeb/Painting/DisplayListResourceStorage.h>
namespace Compositor {
class ConnectionFromWebContent final
: public IPC::ConnectionFromClient<CompositorWebContentClientEndpoint, CompositorWebContentServerEndpoint> {
C_OBJECT(ConnectionFromWebContent)
: public IPC::ConnectionFromClient<CompositorWebContentClientEndpoint, CompositorWebContentServerEndpoint>
, public CompositorStateWebContentClient {
C_OBJECT(ConnectionFromWebContent);
public:
virtual void die() override;
Function<void(ConnectionFromWebContent&)> on_death;
virtual ~ConnectionFromWebContent() override = default;
void notify_compositor_lost();
void set_on_death(Function<void(ConnectionFromWebContent&)> handler) { m_on_death = move(handler); }
private:
explicit ConnectionFromWebContent(NonnullOwnPtr<IPC::Transport>, int client_id);
explicit ConnectionFromWebContent(NonnullOwnPtr<IPC::Transport>, NonnullRefPtr<CompositorState>, int client_id);
virtual void ping() override;
virtual void die() override;
virtual void set_presentation_mode(Web::Compositor::CompositorContextId, Web::Compositor::PresentationMode) override;
virtual void stop_presenting_to_client(Web::Compositor::CompositorContextId) override;
virtual void destroy_context(Web::Compositor::CompositorContextId) override;
virtual void update_display_list(Web::Compositor::CompositorContextId, NonnullRefPtr<Web::Painting::DisplayList>, Web::Painting::DisplayListResourceTransaction, Web::Painting::ScrollStateSnapshot) override;
virtual void update_scroll_state(Web::Compositor::CompositorContextId, Web::Painting::ScrollStateSnapshot) override;
virtual void update_video_frame(Web::Compositor::CompositorContextId, Web::Painting::VideoFrameResourceId, NonnullRefPtr<Media::VideoFrame const>) override;
virtual void clear_video_frame(Web::Compositor::CompositorContextId, Web::Painting::VideoFrameResourceId) override;
virtual void update_compositor_surface(Web::Compositor::CompositorContextId, Web::Painting::CompositorSurfaceId, Gfx::SharedImage) override;
virtual void clear_compositor_surface(Web::Compositor::CompositorContextId, Web::Painting::CompositorSurfaceId) override;
virtual void invalidate_wheel_event_listener_state(Web::Compositor::CompositorContextId, u64 generation) override;
virtual Messages::CompositorWebContentServer::AsyncScrollByResponse async_scroll_by(Web::Compositor::CompositorContextId, Web::UniqueNodeID document_id, Gfx::FloatPoint position, Gfx::FloatPoint delta, Gfx::IntRect viewport_rect, Web::Compositor::AsyncScrollOperationTracking) override;
virtual Messages::CompositorWebContentServer::ShouldDeferMainThreadPresentForAsyncScrollResponse should_defer_main_thread_present_for_async_scroll(Web::Compositor::CompositorContextId) override;
virtual Messages::CompositorWebContentServer::TakePendingAsyncScrollUpdatesResponse take_pending_async_scroll_updates(Web::Compositor::CompositorContextId) override;
virtual void viewport_size_updated(Web::Compositor::CompositorContextId, Gfx::IntSize viewport_size, bool is_top_level_traversable, Web::Compositor::WindowResizingInProgress) override;
virtual void present_frame(Web::Compositor::CompositorContextId, Gfx::IntRect viewport_rect) override;
virtual void request_screenshot(Web::Compositor::CompositorContextId, Web::Compositor::ScreenshotRequestId request_id, Gfx::ShareableBitmap target_bitmap) override;
virtual void dispatch_mouse_event_to_web_content(u64 page_id, Web::MouseEvent const&) override;
virtual void request_rendering_update() override;
void verify_context_is_owned_by_this_connection(Web::Compositor::CompositorContextId);
NonnullRefPtr<CompositorState> m_compositor_state;
Function<void(ConnectionFromWebContent&)> m_on_death;
};
}

View file

@ -10,5 +10,6 @@ namespace Compositor {
class ConnectionFromClient;
class ConnectionFromWebContent;
class CompositorState;
}

View file

@ -43,11 +43,11 @@ ErrorOr<int> ladybird_main(Main::Arguments arguments)
if (!force_cpu_painting)
Gfx::SkiaBackendContext::initialize_gpu_backend();
auto skia_backend_context = Gfx::SkiaBackendContext::the_main_thread_context();
Core::EventLoop event_loop;
auto client = TRY(IPC::take_over_accepted_client_from_system_server<Compositor::ConnectionFromClient>(mach_server_name));
(void)client;
(void)disable_async_scrolling;
auto client = TRY(IPC::take_over_accepted_client_from_system_server<Compositor::ConnectionFromClient>(
mach_server_name, move(skia_backend_context), !disable_async_scrolling));
return event_loop.exec();
}

View file

@ -6,6 +6,12 @@
#include <WebContent/CompositorConnection.h>
#include <LibCore/EventLoop.h>
#include <LibGfx/Bitmap.h>
#include <LibGfx/PaintingSurface.h>
#include <LibWeb/HTML/EventLoop/EventLoop.h>
#include <WebContent/WebContentCompositorHost.h>
namespace WebContent {
CompositorConnection::CompositorConnection(NonnullOwnPtr<IPC::Transport> transport)
@ -15,10 +21,143 @@ CompositorConnection::CompositorConnection(NonnullOwnPtr<IPC::Transport> transpo
void CompositorConnection::die()
{
// The compositor process owns this WebContent's painting and presentation.
// WebContent cannot continue without that connection, but the browser
// process owns recovery and already treats an active renderer exit as a
// crash. Avoid reporting an assertion failure during browser shutdown.
Core::EventLoop::current().quit(0);
}
void CompositorConnection::did_connect()
void CompositorConnection::set_presentation_mode(Web::Compositor::CompositorContextId context_id, Web::Compositor::PresentationMode const& presentation_mode)
{
async_set_presentation_mode(context_id, presentation_mode);
}
void CompositorConnection::stop_presenting_to_client(Web::Compositor::CompositorContextId context_id)
{
async_stop_presenting_to_client(context_id);
}
void CompositorConnection::destroy_context(Web::Compositor::CompositorContextId context_id)
{
async_destroy_context(context_id);
}
void CompositorConnection::update_display_list(Web::Compositor::CompositorContextId context_id, NonnullRefPtr<Web::Painting::DisplayList> const& display_list, Web::Painting::DisplayListResourceTransaction const& resource_transaction, Web::Painting::ScrollStateSnapshot const& scroll_state_snapshot)
{
auto encoded_message = MUST(Messages::CompositorWebContentServer::UpdateDisplayList::static_encode(context_id, display_list, resource_transaction, scroll_state_snapshot));
MUST(post_message(encoded_message));
}
void CompositorConnection::update_scroll_state(Web::Compositor::CompositorContextId context_id, Web::Painting::ScrollStateSnapshot const& scroll_state_snapshot)
{
async_update_scroll_state(context_id, scroll_state_snapshot);
}
void CompositorConnection::update_video_frame(Web::Compositor::CompositorContextId context_id, Web::Painting::VideoFrameResourceId frame_id, NonnullRefPtr<Media::VideoFrame const> const& frame)
{
auto encoded_message = MUST(Messages::CompositorWebContentServer::UpdateVideoFrame::static_encode(context_id, frame_id, frame));
MUST(post_message(encoded_message));
}
void CompositorConnection::clear_video_frame(Web::Compositor::CompositorContextId context_id, Web::Painting::VideoFrameResourceId frame_id)
{
async_clear_video_frame(context_id, frame_id);
}
void CompositorConnection::update_compositor_surface(Web::Compositor::CompositorContextId context_id, Web::Painting::CompositorSurfaceId surface_id, Gfx::SharedImage const& shared_image)
{
async_update_compositor_surface(context_id, surface_id, shared_image);
}
void CompositorConnection::clear_compositor_surface(Web::Compositor::CompositorContextId context_id, Web::Painting::CompositorSurfaceId surface_id)
{
async_clear_compositor_surface(context_id, surface_id);
}
void CompositorConnection::invalidate_wheel_event_listener_state(Web::Compositor::CompositorContextId context_id, u64 generation)
{
async_invalidate_wheel_event_listener_state(context_id, generation);
}
Web::Compositor::AsyncScrollEnqueueResult CompositorConnection::async_scroll_by(Web::Compositor::CompositorContextId context_id, Web::UniqueNodeID document_id, Gfx::FloatPoint position, Gfx::FloatPoint delta, Gfx::IntRect viewport_rect, Web::Compositor::AsyncScrollOperationTracking operation_tracking)
{
auto response = send_sync<Messages::CompositorWebContentServer::AsyncScrollBy>(context_id, document_id, position, delta, viewport_rect, operation_tracking);
return response->take_result();
}
bool CompositorConnection::should_defer_main_thread_present_for_async_scroll(Web::Compositor::CompositorContextId context_id)
{
auto response = send_sync<Messages::CompositorWebContentServer::ShouldDeferMainThreadPresentForAsyncScroll>(context_id);
return response->should_defer();
}
Web::Compositor::PendingAsyncScrollUpdates CompositorConnection::take_pending_async_scroll_updates(Web::Compositor::CompositorContextId context_id)
{
auto response = send_sync<Messages::CompositorWebContentServer::TakePendingAsyncScrollUpdates>(context_id);
return response->take_updates();
}
void CompositorConnection::viewport_size_updated(Web::Compositor::CompositorContextId context_id, Gfx::IntSize viewport_size, bool is_top_level_traversable, Web::Compositor::WindowResizingInProgress window_resize_in_progress)
{
async_viewport_size_updated(context_id, viewport_size, is_top_level_traversable, window_resize_in_progress);
}
void CompositorConnection::present_frame(Web::Compositor::CompositorContextId context_id, Gfx::IntRect viewport_rect)
{
async_present_frame(context_id, viewport_rect);
}
void CompositorConnection::request_screenshot(Web::Compositor::CompositorContextId context_id, NonnullRefPtr<Gfx::PaintingSurface> target_surface, Function<void()>&& callback)
{
auto target_bitmap = MUST(Gfx::Bitmap::create_shareable(Gfx::BitmapFormat::BGRA8888, Gfx::AlphaType::Premultiplied, target_surface->size()));
auto shareable_bitmap = Gfx::ShareableBitmap { target_bitmap, Gfx::ShareableBitmap::ConstructWithKnownGoodBitmap };
auto request_id = Web::Compositor::ScreenshotRequestId { m_next_screenshot_request_id++ };
m_screenshots.set(request_id, PendingScreenshot { move(target_surface), move(target_bitmap), move(callback) });
async_request_screenshot(context_id, request_id, move(shareable_bitmap));
}
void CompositorConnection::mouse_event(u64 page_id, Web::MouseEvent event)
{
if (on_mouse_event)
on_mouse_event(page_id, move(event));
}
void CompositorConnection::request_rendering_update()
{
Web::HTML::main_thread_event_loop().queue_task_to_update_the_rendering();
}
void CompositorConnection::did_complete_screenshot(Web::Compositor::ScreenshotRequestId request_id)
{
auto pending_screenshot = take_screenshot(request_id);
VERIFY(pending_screenshot.has_value());
pending_screenshot->target_surface->write_from_bitmap(*pending_screenshot->target_bitmap);
if (pending_screenshot->callback)
pending_screenshot->callback();
}
void CompositorConnection::did_fail_screenshot(Web::Compositor::ScreenshotRequestId request_id)
{
auto pending_screenshot = take_screenshot(request_id);
VERIFY(pending_screenshot.has_value());
if (pending_screenshot->callback)
pending_screenshot->callback();
}
void CompositorConnection::did_lose_compositor()
{
// The Compositor service sends this when its browser-process control
// connection is gone. WebContent cannot present without that owner either,
// so exit cleanly instead of reporting a renderer crash during shutdown.
die();
}
Optional<CompositorConnection::PendingScreenshot> CompositorConnection::take_screenshot(Web::Compositor::ScreenshotRequestId request_id)
{
return m_screenshots.take(request_id);
}
}

View file

@ -6,9 +6,25 @@
#pragma once
#include <AK/Function.h>
#include <AK/HashMap.h>
#include <AK/NonnullRefPtr.h>
#include <AK/Optional.h>
#include <Compositor/CompositorWebContentClientEndpoint.h>
#include <Compositor/CompositorWebContentServerEndpoint.h>
#include <LibGfx/Forward.h>
#include <LibGfx/Point.h>
#include <LibGfx/Rect.h>
#include <LibGfx/ShareableBitmap.h>
#include <LibGfx/SharedImage.h>
#include <LibGfx/Size.h>
#include <LibIPC/ConnectionToServer.h>
#include <LibMedia/Forward.h>
#include <LibWeb/Compositor/Types.h>
#include <LibWeb/Page/InputEvent.h>
#include <LibWeb/Painting/DisplayList.h>
#include <LibWeb/Painting/DisplayListResourceStorage.h>
#include <LibWeb/Painting/ScrollState.h>
namespace WebContent {
@ -20,10 +36,44 @@ class CompositorConnection final
public:
explicit CompositorConnection(NonnullOwnPtr<IPC::Transport>);
void set_presentation_mode(Web::Compositor::CompositorContextId, Web::Compositor::PresentationMode const&);
void stop_presenting_to_client(Web::Compositor::CompositorContextId);
void destroy_context(Web::Compositor::CompositorContextId);
void update_display_list(Web::Compositor::CompositorContextId, NonnullRefPtr<Web::Painting::DisplayList> const&, Web::Painting::DisplayListResourceTransaction const&, Web::Painting::ScrollStateSnapshot const&);
void update_scroll_state(Web::Compositor::CompositorContextId, Web::Painting::ScrollStateSnapshot const&);
void update_video_frame(Web::Compositor::CompositorContextId, Web::Painting::VideoFrameResourceId, NonnullRefPtr<Media::VideoFrame const> const&);
void clear_video_frame(Web::Compositor::CompositorContextId, Web::Painting::VideoFrameResourceId);
void update_compositor_surface(Web::Compositor::CompositorContextId, Web::Painting::CompositorSurfaceId, Gfx::SharedImage const&);
void clear_compositor_surface(Web::Compositor::CompositorContextId, Web::Painting::CompositorSurfaceId);
void invalidate_wheel_event_listener_state(Web::Compositor::CompositorContextId, u64 generation);
Web::Compositor::AsyncScrollEnqueueResult async_scroll_by(Web::Compositor::CompositorContextId, Web::UniqueNodeID document_id, Gfx::FloatPoint position, Gfx::FloatPoint delta, Gfx::IntRect viewport_rect, Web::Compositor::AsyncScrollOperationTracking);
bool should_defer_main_thread_present_for_async_scroll(Web::Compositor::CompositorContextId);
Web::Compositor::PendingAsyncScrollUpdates take_pending_async_scroll_updates(Web::Compositor::CompositorContextId);
void viewport_size_updated(Web::Compositor::CompositorContextId, Gfx::IntSize, bool is_top_level_traversable, Web::Compositor::WindowResizingInProgress);
void present_frame(Web::Compositor::CompositorContextId, Gfx::IntRect);
void request_screenshot(Web::Compositor::CompositorContextId, NonnullRefPtr<Gfx::PaintingSurface>, Function<void()>&&);
Function<void(u64 page_id, Web::MouseEvent)> on_mouse_event;
private:
struct PendingScreenshot {
NonnullRefPtr<Gfx::PaintingSurface> target_surface;
NonnullRefPtr<Gfx::Bitmap> target_bitmap;
Function<void()> callback;
};
virtual void die() override;
virtual void did_connect() override;
virtual void mouse_event(u64 page_id, Web::MouseEvent) override;
virtual void request_rendering_update() override;
virtual void did_complete_screenshot(Web::Compositor::ScreenshotRequestId) override;
virtual void did_fail_screenshot(Web::Compositor::ScreenshotRequestId) override;
virtual void did_lose_compositor() override;
Optional<PendingScreenshot> take_screenshot(Web::Compositor::ScreenshotRequestId);
HashMap<Web::Compositor::ScreenshotRequestId, PendingScreenshot> m_screenshots;
u64 m_next_screenshot_request_id { 1 };
};
}