2026-05-22 11:02:42 -03:00
|
|
|
/*
|
|
|
|
|
* Copyright (c) 2026, the Ladybird developers.
|
|
|
|
|
*
|
|
|
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include <Compositor/ConnectionFromWebContent.h>
|
2026-06-14 19:13:19 -03:00
|
|
|
#include <LibCore/System.h>
|
2026-05-22 09:00:41 -03:00
|
|
|
#include <LibWeb/Page/InputEvent.h>
|
2026-05-22 11:02:42 -03:00
|
|
|
|
|
|
|
|
namespace Compositor {
|
|
|
|
|
|
2026-05-22 09:00:41 -03:00
|
|
|
ConnectionFromWebContent::ConnectionFromWebContent(NonnullOwnPtr<IPC::Transport> transport, NonnullRefPtr<CompositorState> compositor_state, int client_id)
|
2026-05-22 11:02:42 -03:00
|
|
|
: IPC::ConnectionFromClient<CompositorWebContentClientEndpoint, CompositorWebContentServerEndpoint>(*this, move(transport), client_id)
|
2026-05-22 09:00:41 -03:00
|
|
|
, m_compositor_state(move(compositor_state))
|
2026-06-15 14:29:43 -03:00
|
|
|
, m_canvas_host(m_compositor_state->skia_backend_context(), m_compositor_state->canvas_surface_registry())
|
2026-05-22 11:02:42 -03:00
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ConnectionFromWebContent::die()
|
|
|
|
|
{
|
2026-05-22 09:00:41 -03:00
|
|
|
auto protector = NonnullRefPtr { *this };
|
|
|
|
|
m_compositor_state->destroy_contexts_for_web_content_client(*this);
|
|
|
|
|
if (m_on_death)
|
|
|
|
|
m_on_death(*this);
|
2026-05-22 11:02:42 -03:00
|
|
|
}
|
|
|
|
|
|
2026-05-22 09:00:41 -03:00
|
|
|
void ConnectionFromWebContent::notify_compositor_lost()
|
2026-05-22 11:02:42 -03:00
|
|
|
{
|
2026-05-22 09:00:41 -03:00
|
|
|
async_did_lose_compositor();
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-14 19:13:19 -03:00
|
|
|
Messages::CompositorWebContentServer::InitTransportResponse ConnectionFromWebContent::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();
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-22 09:00:41 -03:00
|
|
|
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();
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-18 01:10:37 -03:00
|
|
|
void ConnectionFromWebContent::set_parent_context(Web::Compositor::CompositorContextId context_id, Optional<Web::Compositor::CompositorContextId> parent_context_id)
|
2026-05-22 09:00:41 -03:00
|
|
|
{
|
|
|
|
|
verify_context_is_owned_by_this_connection(context_id);
|
2026-06-18 01:10:37 -03:00
|
|
|
m_compositor_state->set_parent_context(context_id, parent_context_id);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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);
|
2026-05-22 09:00:41 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-27 12:51:03 -03:00
|
|
|
void ConnectionFromWebContent::update_display_list(Web::Compositor::CompositorContextId context_id, NonnullRefPtr<Web::Painting::DisplayList> display_list, Web::Painting::AccumulatedVisualContextTree visual_context_tree, Web::Painting::DisplayListResourceTransaction resource_transaction, Web::Painting::ScrollStateSnapshot scroll_state_snapshot)
|
2026-05-22 09:00:41 -03:00
|
|
|
{
|
|
|
|
|
verify_context_is_owned_by_this_connection(context_id);
|
2026-05-27 12:51:03 -03:00
|
|
|
m_compositor_state->update_display_list(context_id, move(display_list), move(visual_context_tree), move(resource_transaction), move(scroll_state_snapshot));
|
2026-05-22 09:00:41 -03:00
|
|
|
}
|
|
|
|
|
|
2026-06-02 15:39:40 -03:00
|
|
|
void ConnectionFromWebContent::update_visual_context_tree(Web::Compositor::CompositorContextId context_id, Web::Painting::AccumulatedVisualContextTree visual_context_tree)
|
|
|
|
|
{
|
|
|
|
|
verify_context_is_owned_by_this_connection(context_id);
|
|
|
|
|
m_compositor_state->update_visual_context_tree(context_id, move(visual_context_tree));
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-22 09:00:41 -03:00
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-15 14:29:43 -03:00
|
|
|
Messages::CompositorWebContentServer::CreateCanvas2dContextResponse ConnectionFromWebContent::create_canvas_2d_context(Gfx::IntSize size, bool alpha)
|
|
|
|
|
{
|
|
|
|
|
auto canvas_id = m_canvas_host.create_2d_context(size, alpha);
|
|
|
|
|
if (!canvas_id.has_value())
|
|
|
|
|
return { false, Web::Painting::CanvasId { 0 } };
|
|
|
|
|
return { true, *canvas_id };
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-18 18:12:08 -03:00
|
|
|
void ConnectionFromWebContent::update_canvas_2d_commands(Web::Painting::CanvasId canvas_id, Gfx::CanvasCommandList commands, bool commit)
|
2026-06-15 14:29:43 -03:00
|
|
|
{
|
2026-06-18 18:12:08 -03:00
|
|
|
m_canvas_host.execute_canvas_2d_commands(canvas_id, commands, commit);
|
2026-06-15 14:29:43 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ConnectionFromWebContent::destroy_canvas_context(Web::Painting::CanvasId canvas_id)
|
|
|
|
|
{
|
|
|
|
|
m_canvas_host.destroy_context(canvas_id);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Messages::CompositorWebContentServer::GetCanvasPixelsResponse ConnectionFromWebContent::get_canvas_pixels(Web::Painting::CanvasId canvas_id, Gfx::IntRect rect)
|
|
|
|
|
{
|
|
|
|
|
return m_canvas_host.read_back_pixels(canvas_id, rect);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Messages::CompositorWebContentServer::CreateWebglContextResponse ConnectionFromWebContent::create_webgl_context(Web::WebGL::WebGLVersion webgl_version, Gfx::IntSize size, bool depth, bool stencil, bool antialias)
|
|
|
|
|
{
|
|
|
|
|
auto result = m_canvas_host.create_webgl_context(webgl_version, size, depth, stencil, antialias);
|
|
|
|
|
return { result.success, result.canvas_id, move(result.supported_extensions) };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ConnectionFromWebContent::webgl_commands(Web::Painting::CanvasId canvas_id, ByteBuffer commands, Vector<Gfx::DecodedImageFrame> bitmaps)
|
|
|
|
|
{
|
|
|
|
|
m_canvas_host.execute_webgl_commands(canvas_id, commands, bitmaps);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ConnectionFromWebContent::webgl_present_canvas(Web::Painting::CanvasId canvas_id, bool preserve_drawing_buffer)
|
|
|
|
|
{
|
|
|
|
|
m_canvas_host.present_webgl_canvas(canvas_id, preserve_drawing_buffer);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Messages::CompositorWebContentServer::WebglSyncCallResponse ConnectionFromWebContent::webgl_sync_call(Web::Painting::CanvasId canvas_id, ByteBuffer request)
|
|
|
|
|
{
|
|
|
|
|
return MUST(m_canvas_host.execute_webgl_sync_call(canvas_id, move(request)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Messages::CompositorWebContentServer::WebglReadPixelsResponse ConnectionFromWebContent::webgl_read_pixels(Web::Painting::CanvasId canvas_id, i32 x, i32 y, i32 width, i32 height, u32 format, u32 type, i32 buf_size, Core::AnonymousBuffer pixels)
|
|
|
|
|
{
|
|
|
|
|
if (buf_size < 0 || (buf_size > 0 && (!pixels.is_valid() || pixels.size() < static_cast<size_t>(buf_size)))) {
|
|
|
|
|
did_misbehave("WebContent sent an invalid WebGL readPixels buffer");
|
|
|
|
|
return { 0, 0, 0 };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto result = m_canvas_host.webgl_read_pixels_robust_angle(canvas_id, x, y, width, height, format, type, buf_size, move(pixels));
|
|
|
|
|
return { result.length, result.columns, result.rows };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ConnectionFromWebContent::webgl_read_buffer_sub_data(Web::Painting::CanvasId canvas_id, u32 target, i64 offset, i64 size, Core::AnonymousBuffer data)
|
|
|
|
|
{
|
|
|
|
|
if (size < 0 || (size > 0 && (!data.is_valid() || data.size() < static_cast<size_t>(size)))) {
|
|
|
|
|
did_misbehave("WebContent sent an invalid WebGL buffer readback target");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_canvas_host.webgl_read_buffer_sub_data(canvas_id, target, offset, size, move(data));
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-22 09:00:41 -03:00
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-27 14:26:14 -03:00
|
|
|
void ConnectionFromWebContent::viewport_size_updated(Web::Compositor::CompositorContextId context_id, Gfx::IntSize viewport_size, Web::Compositor::WindowResizingInProgress window_resize_in_progress)
|
2026-05-22 09:00:41 -03:00
|
|
|
{
|
|
|
|
|
verify_context_is_owned_by_this_connection(context_id);
|
2026-05-27 14:26:14 -03:00
|
|
|
m_compositor_state->viewport_size_updated(context_id, viewport_size, window_resize_in_progress);
|
2026-05-22 09:00:41 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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);
|
2026-05-22 11:02:42 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|