ladybird/Libraries/LibWeb/Compositor/AsyncScrollingState.h
Aliaksandr Kalenik 17f5865d36 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.
2026-05-22 19:50:42 +01:00

167 lines
6.3 KiB
C++

/*
* Copyright (c) 2026-present, the Ladybird developers.
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/Optional.h>
#include <AK/StringView.h>
#include <AK/Types.h>
#include <AK/Vector.h>
#include <LibGfx/Color.h>
#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>
namespace Web::Compositor {
using AsyncScrollOperationID = u64;
// Stable identifier for a scroll frame in a document; the frame index alone is not unique across nested documents.
struct AsyncScrollNodeID {
UniqueNodeID document_id;
Painting::ScrollFrameIndex scroll_frame_index;
bool operator==(AsyncScrollNodeID const&) const = default;
};
enum class AsyncScrollNodeKind : u8 {
Viewport,
Element,
PseudoElement,
};
// Stable identity for reconciling compositor-side scroll offsets after the paint snapshot has been rebuilt.
struct AsyncScrollNodeStableID {
UniqueNodeID node_id;
AsyncScrollNodeKind kind { AsyncScrollNodeKind::Element };
u8 pseudo_element_type { 0 };
bool operator==(AsyncScrollNodeStableID const&) const = default;
};
struct AsyncScrollOffset {
AsyncScrollNodeStableID stable_node_id;
Gfx::FloatPoint compositor_scroll_offset;
Gfx::FloatPoint unadopted_scroll_delta;
};
// One scrollable area from the paint snapshot. Non-viewport scrollports are stored in hit_test_visual_context_index
// coordinates and transformed to viewport coordinates when the compositor rebuilds wheel targets.
struct AsyncScrollNode {
AsyncScrollNodeID node_id;
AsyncScrollNodeStableID stable_node_id;
Optional<AsyncScrollNodeID> parent_node_id;
Painting::VisualContextIndex hit_test_visual_context_index;
Gfx::IntRect scrollport_rect;
Gfx::FloatPoint max_scroll_offset;
bool is_viewport { false };
bool can_be_wheel_scrolled_horizontally { false };
bool can_be_wheel_scrolled_vertically { false };
};
// Sticky elements are represented as scroll frames whose offset is derived from ancestor scroll offsets. Keep only
// the precomputed geometry needed to replay that calculation on the compositor thread after an async scroll mutation.
struct AsyncStickyArea {
UniqueNodeID document_id;
Painting::ScrollFrameIndex scroll_frame_index;
Painting::ScrollFrameIndex parent_scroll_frame_index;
Painting::ScrollFrameIndex nearest_scrolling_ancestor_index;
Gfx::FloatPoint position_relative_to_scroll_ancestor;
Gfx::FloatSize border_box_size;
Gfx::FloatSize scrollport_size;
Gfx::FloatRect containing_block_region;
bool needs_parent_offset_adjustment { false };
Optional<float> inset_top;
Optional<float> inset_right;
Optional<float> inset_bottom;
Optional<float> inset_left;
};
// A region with a non-passive wheel listener. Wheels inside it must stay on the main thread because script may cancel.
struct BlockingWheelEventRegion {
Painting::VisualContextIndex visual_context_index;
Gfx::FloatRect rect;
};
struct WheelHitTestTarget {
Painting::VisualContextIndex visual_context_index;
Gfx::FloatRect rect;
Gfx::CornerRadii corner_radii;
Optional<AsyncScrollNodeID> target_node_id;
};
// A region that must always use main-thread wheel routing even without a blocking listener, such as a nested navigable.
struct MainThreadWheelEventRegion {
Painting::VisualContextIndex visual_context_index;
Gfx::FloatRect rect;
};
struct ViewportScrollbar {
AsyncScrollNodeID scroll_node_id;
Painting::ScrollFrameIndex scroll_frame_index;
Gfx::IntRect gutter_rect;
Gfx::IntRect thumb_rect;
Gfx::IntRect expanded_gutter_rect;
Gfx::IntRect expanded_thumb_rect;
double scroll_size { 0 };
double expanded_scroll_size { 0 };
float max_scroll_offset { 0 };
Color thumb_color;
Color track_color;
bool vertical { false };
};
struct AsyncScrollingState {
Vector<AsyncScrollNode> scroll_nodes;
Vector<AsyncStickyArea> sticky_areas;
Vector<WheelHitTestTarget> wheel_hit_test_targets;
Vector<MainThreadWheelEventRegion> main_thread_wheel_event_regions;
Vector<ViewportScrollbar> viewport_scrollbars;
// Non-passive wheel listeners can cancel scrolling, so async scrolling must treat them as hard barriers.
// Viewport-wide barriers cover listeners on the root targets; element regions let input hit-testing accept
// async scrolling elsewhere.
Vector<BlockingWheelEventRegion> blocking_wheel_event_regions;
Gfx::IntRect viewport_rect;
// Bumped whenever wheel listener state changes so queued compositor snapshots
// cannot re-enable async wheel routing after a non-passive listener has been
// added.
u64 wheel_event_listener_state_generation { 0 };
bool has_blocking_wheel_event_listeners { false };
bool has_blocking_wheel_event_region_covering_viewport { false };
};
enum class WheelRoutingAdmission {
Accepted,
NoAsyncScrollingState,
BlockingWheelEventListeners,
NoScrollNode,
StaleWheelEventListeners,
};
enum class WheelScrollAdmission {
Accepted,
NoScrollableTarget,
BlockedByMainThreadRegion,
StaleBlockingWheelEventRegions,
BlockedByWheelEventRegion,
};
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);
}