LibWeb: Add compositor scroll state snapshots
Record the scroll geometry that CompositorThread can safely reason about
while the main thread is painting. The snapshot contains stable scroll
node IDs, parent links, scroll bounds, sticky inputs, and wheel blocker
regions in display-list coordinate space.
Add AsyncScrollingState and AsyncScrollTree under LibWeb/Compositor. The
state is the immutable main-thread snapshot; the tree is the mutable
compositor-side copy that can replay scroll deltas and sticky offsets
without touching DOM, layout, or paintables.
Expose the state through internals and add text tests for tree shape,
parent links, sticky areas, blocker hit testing, nested scrollers, and
admission decisions. Keep the directory skipped unless the feature is
enabled with --enable-async-scrolling.
2026-05-11 16:27:11 -03:00
|
|
|
/*
|
|
|
|
|
* Copyright (c) 2026-present, the Ladybird developers.
|
|
|
|
|
*
|
|
|
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
|
|
|
*/
|
|
|
|
|
|
2026-05-13 08:13:38 -03:00
|
|
|
#include <LibWeb/Compositor/AsyncScrollTree.h>
|
LibWeb: Add compositor scroll state snapshots
Record the scroll geometry that CompositorThread can safely reason about
while the main thread is painting. The snapshot contains stable scroll
node IDs, parent links, scroll bounds, sticky inputs, and wheel blocker
regions in display-list coordinate space.
Add AsyncScrollingState and AsyncScrollTree under LibWeb/Compositor. The
state is the immutable main-thread snapshot; the tree is the mutable
compositor-side copy that can replay scroll deltas and sticky offsets
without touching DOM, layout, or paintables.
Expose the state through internals and add text tests for tree shape,
parent links, sticky areas, blocker hit testing, nested scrollers, and
admission decisions. Keep the directory skipped unless the feature is
enabled with --enable-async-scrolling.
2026-05-11 16:27:11 -03:00
|
|
|
#include <LibWeb/Compositor/AsyncScrollingState.h>
|
|
|
|
|
#include <LibWeb/Painting/DisplayList.h>
|
|
|
|
|
#include <LibWeb/Painting/ScrollState.h>
|
2026-05-14 11:20:06 -03:00
|
|
|
|
LibWeb: Add compositor scroll state snapshots
Record the scroll geometry that CompositorThread can safely reason about
while the main thread is painting. The snapshot contains stable scroll
node IDs, parent links, scroll bounds, sticky inputs, and wheel blocker
regions in display-list coordinate space.
Add AsyncScrollingState and AsyncScrollTree under LibWeb/Compositor. The
state is the immutable main-thread snapshot; the tree is the mutable
compositor-side copy that can replay scroll deltas and sticky offsets
without touching DOM, layout, or paintables.
Expose the state through internals and add text tests for tree shape,
parent links, sticky areas, blocker hit testing, nested scrollers, and
admission decisions. Keep the directory skipped unless the feature is
enabled with --enable-async-scrolling.
2026-05-11 16:27:11 -03:00
|
|
|
namespace Web::Compositor {
|
|
|
|
|
|
2026-05-13 08:13:38 -03:00
|
|
|
static AsyncScrollNodeID scroll_node_id_for(UniqueNodeID document_id, Painting::ScrollFrameIndex scroll_frame_index)
|
|
|
|
|
{
|
|
|
|
|
return { .document_id = document_id, .scroll_frame_index = scroll_frame_index };
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-14 11:20:06 -03:00
|
|
|
static AsyncScrollNodeKind async_scroll_node_kind_for(Painting::CompositorScrollNodeKind kind)
|
|
|
|
|
{
|
|
|
|
|
switch (kind) {
|
|
|
|
|
case Painting::CompositorScrollNodeKind::Viewport:
|
|
|
|
|
return AsyncScrollNodeKind::Viewport;
|
|
|
|
|
case Painting::CompositorScrollNodeKind::Element:
|
|
|
|
|
return AsyncScrollNodeKind::Element;
|
|
|
|
|
case Painting::CompositorScrollNodeKind::PseudoElement:
|
|
|
|
|
return AsyncScrollNodeKind::PseudoElement;
|
|
|
|
|
}
|
|
|
|
|
VERIFY_NOT_REACHED();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static AsyncScrollNodeStableID stable_scroll_node_id_for(UniqueNodeID scrollable_node_id, Painting::CompositorScrollNodeKind kind, u8 pseudo_element_type)
|
|
|
|
|
{
|
|
|
|
|
return {
|
|
|
|
|
.node_id = scrollable_node_id,
|
|
|
|
|
.kind = async_scroll_node_kind_for(kind),
|
|
|
|
|
.pseudo_element_type = pseudo_element_type,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-13 08:13:38 -03:00
|
|
|
AsyncScrollingState async_scrolling_state_from_display_list(Painting::DisplayList const& display_list)
|
|
|
|
|
{
|
|
|
|
|
AsyncScrollingState async_scrolling_state;
|
|
|
|
|
Vector<Painting::ScrollFrameIndex> parent_scroll_frame_indices;
|
2026-05-14 07:22:26 -03:00
|
|
|
Vector<Painting::ScrollFrameIndex> wheel_hit_test_target_scroll_frame_indices;
|
|
|
|
|
Vector<UniqueNodeID> wheel_hit_test_target_document_ids;
|
2026-05-13 08:13:38 -03:00
|
|
|
|
|
|
|
|
if (auto const& metadata = display_list.async_scrolling_metadata(); metadata.has_value()) {
|
|
|
|
|
async_scrolling_state.viewport_rect = metadata->viewport_rect;
|
|
|
|
|
async_scrolling_state.wheel_event_listener_state_generation = metadata->wheel_event_listener_state_generation;
|
|
|
|
|
async_scrolling_state.has_blocking_wheel_event_listeners = metadata->has_blocking_wheel_event_listeners;
|
|
|
|
|
async_scrolling_state.has_blocking_wheel_event_region_covering_viewport = metadata->has_blocking_wheel_event_region_covering_viewport;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
display_list.for_each_command_header([&](Painting::DisplayListCommandHeader const& header, ReadonlyBytes payload) {
|
|
|
|
|
switch (header.type) {
|
|
|
|
|
case Painting::DisplayListCommandType::CompositorBlockingWheelEventRegion: {
|
|
|
|
|
auto command = Painting::read_display_list_command_payload<Painting::CompositorBlockingWheelEventRegion>(payload);
|
|
|
|
|
async_scrolling_state.has_blocking_wheel_event_listeners = true;
|
|
|
|
|
async_scrolling_state.blocking_wheel_event_regions.append({
|
|
|
|
|
.visual_context_index = header.context_index,
|
|
|
|
|
.rect = command.rect,
|
|
|
|
|
});
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case Painting::DisplayListCommandType::CompositorStickyArea: {
|
|
|
|
|
auto command = Painting::read_display_list_command_payload<Painting::CompositorStickyArea>(payload);
|
|
|
|
|
async_scrolling_state.sticky_areas.append({
|
|
|
|
|
.document_id = command.document_id,
|
|
|
|
|
.scroll_frame_index = command.scroll_frame_index,
|
|
|
|
|
.parent_scroll_frame_index = command.parent_scroll_frame_index,
|
|
|
|
|
.nearest_scrolling_ancestor_index = command.nearest_scrolling_ancestor_index,
|
|
|
|
|
.position_relative_to_scroll_ancestor = command.position_relative_to_scroll_ancestor,
|
|
|
|
|
.border_box_size = command.border_box_size,
|
|
|
|
|
.scrollport_size = command.scrollport_size,
|
|
|
|
|
.containing_block_region = command.containing_block_region,
|
|
|
|
|
.needs_parent_offset_adjustment = command.needs_parent_offset_adjustment,
|
|
|
|
|
.inset_top = command.inset_top,
|
|
|
|
|
.inset_right = command.inset_right,
|
|
|
|
|
.inset_bottom = command.inset_bottom,
|
|
|
|
|
.inset_left = command.inset_left,
|
|
|
|
|
});
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case Painting::DisplayListCommandType::CompositorScrollNode: {
|
|
|
|
|
auto command = Painting::read_display_list_command_payload<Painting::CompositorScrollNode>(payload);
|
LibWeb: Add compositor scroll state snapshots
Record the scroll geometry that CompositorThread can safely reason about
while the main thread is painting. The snapshot contains stable scroll
node IDs, parent links, scroll bounds, sticky inputs, and wheel blocker
regions in display-list coordinate space.
Add AsyncScrollingState and AsyncScrollTree under LibWeb/Compositor. The
state is the immutable main-thread snapshot; the tree is the mutable
compositor-side copy that can replay scroll deltas and sticky offsets
without touching DOM, layout, or paintables.
Expose the state through internals and add text tests for tree shape,
parent links, sticky areas, blocker hit testing, nested scrollers, and
admission decisions. Keep the directory skipped unless the feature is
enabled with --enable-async-scrolling.
2026-05-11 16:27:11 -03:00
|
|
|
async_scrolling_state.scroll_nodes.append({
|
2026-05-13 08:13:38 -03:00
|
|
|
.node_id = scroll_node_id_for(command.document_id, command.scroll_frame_index),
|
2026-05-14 11:20:06 -03:00
|
|
|
.stable_node_id = stable_scroll_node_id_for(command.scrollable_node_id, command.scroll_node_kind, command.pseudo_element_type),
|
LibWeb: Add compositor scroll state snapshots
Record the scroll geometry that CompositorThread can safely reason about
while the main thread is painting. The snapshot contains stable scroll
node IDs, parent links, scroll bounds, sticky inputs, and wheel blocker
regions in display-list coordinate space.
Add AsyncScrollingState and AsyncScrollTree under LibWeb/Compositor. The
state is the immutable main-thread snapshot; the tree is the mutable
compositor-side copy that can replay scroll deltas and sticky offsets
without touching DOM, layout, or paintables.
Expose the state through internals and add text tests for tree shape,
parent links, sticky areas, blocker hit testing, nested scrollers, and
admission decisions. Keep the directory skipped unless the feature is
enabled with --enable-async-scrolling.
2026-05-11 16:27:11 -03:00
|
|
|
.parent_node_id = {},
|
2026-05-13 08:13:38 -03:00
|
|
|
.hit_test_visual_context_index = header.context_index,
|
|
|
|
|
.scrollport_rect = command.scrollport_rect,
|
|
|
|
|
.max_scroll_offset = command.max_scroll_offset,
|
|
|
|
|
.is_viewport = command.is_viewport,
|
2026-05-16 19:53:24 -03:00
|
|
|
.can_be_wheel_scrolled_horizontally = command.can_be_wheel_scrolled_horizontally,
|
|
|
|
|
.can_be_wheel_scrolled_vertically = command.can_be_wheel_scrolled_vertically,
|
2026-05-13 08:13:38 -03:00
|
|
|
});
|
|
|
|
|
parent_scroll_frame_indices.append(command.parent_scroll_frame_index);
|
|
|
|
|
break;
|
|
|
|
|
}
|
2026-05-14 07:22:26 -03:00
|
|
|
case Painting::DisplayListCommandType::CompositorWheelHitTestTarget: {
|
|
|
|
|
auto command = Painting::read_display_list_command_payload<Painting::CompositorWheelHitTestTarget>(payload);
|
|
|
|
|
async_scrolling_state.wheel_hit_test_targets.append({
|
|
|
|
|
.visual_context_index = header.context_index,
|
|
|
|
|
.rect = command.rect,
|
|
|
|
|
.corner_radii = command.corner_radii,
|
|
|
|
|
.target_node_id = {},
|
|
|
|
|
});
|
|
|
|
|
wheel_hit_test_target_scroll_frame_indices.append(command.target_scroll_frame_index);
|
|
|
|
|
wheel_hit_test_target_document_ids.append(command.document_id);
|
|
|
|
|
break;
|
|
|
|
|
}
|
2026-05-13 08:13:38 -03:00
|
|
|
case Painting::DisplayListCommandType::CompositorMainThreadWheelEventRegion: {
|
|
|
|
|
auto command = Painting::read_display_list_command_payload<Painting::CompositorMainThreadWheelEventRegion>(payload);
|
|
|
|
|
async_scrolling_state.main_thread_wheel_event_regions.append({
|
|
|
|
|
.visual_context_index = header.context_index,
|
|
|
|
|
.rect = command.rect,
|
LibWeb: Add compositor scroll state snapshots
Record the scroll geometry that CompositorThread can safely reason about
while the main thread is painting. The snapshot contains stable scroll
node IDs, parent links, scroll bounds, sticky inputs, and wheel blocker
regions in display-list coordinate space.
Add AsyncScrollingState and AsyncScrollTree under LibWeb/Compositor. The
state is the immutable main-thread snapshot; the tree is the mutable
compositor-side copy that can replay scroll deltas and sticky offsets
without touching DOM, layout, or paintables.
Expose the state through internals and add text tests for tree shape,
parent links, sticky areas, blocker hit testing, nested scrollers, and
admission decisions. Keep the directory skipped unless the feature is
enabled with --enable-async-scrolling.
2026-05-11 16:27:11 -03:00
|
|
|
});
|
2026-05-13 08:13:38 -03:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case Painting::DisplayListCommandType::CompositorViewportScrollbar: {
|
|
|
|
|
auto command = Painting::read_display_list_command_payload<Painting::CompositorViewportScrollbar>(payload);
|
|
|
|
|
async_scrolling_state.viewport_scrollbars.append({
|
|
|
|
|
.scroll_node_id = scroll_node_id_for(command.document_id, command.scroll_frame_index),
|
|
|
|
|
.scroll_frame_index = command.scroll_frame_index,
|
|
|
|
|
.gutter_rect = command.gutter_rect,
|
|
|
|
|
.thumb_rect = command.thumb_rect,
|
|
|
|
|
.expanded_gutter_rect = command.expanded_gutter_rect,
|
|
|
|
|
.expanded_thumb_rect = command.expanded_thumb_rect,
|
|
|
|
|
.scroll_size = command.scroll_size,
|
|
|
|
|
.expanded_scroll_size = command.expanded_scroll_size,
|
|
|
|
|
.max_scroll_offset = command.max_scroll_offset,
|
|
|
|
|
.thumb_color = command.thumb_color,
|
|
|
|
|
.track_color = command.track_color,
|
|
|
|
|
.vertical = command.vertical,
|
|
|
|
|
});
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
break;
|
LibWeb: Add compositor scroll state snapshots
Record the scroll geometry that CompositorThread can safely reason about
while the main thread is painting. The snapshot contains stable scroll
node IDs, parent links, scroll bounds, sticky inputs, and wheel blocker
regions in display-list coordinate space.
Add AsyncScrollingState and AsyncScrollTree under LibWeb/Compositor. The
state is the immutable main-thread snapshot; the tree is the mutable
compositor-side copy that can replay scroll deltas and sticky offsets
without touching DOM, layout, or paintables.
Expose the state through internals and add text tests for tree shape,
parent links, sticky areas, blocker hit testing, nested scrollers, and
admission decisions. Keep the directory skipped unless the feature is
enabled with --enable-async-scrolling.
2026-05-11 16:27:11 -03:00
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
VERIFY(parent_scroll_frame_indices.size() == async_scrolling_state.scroll_nodes.size());
|
2026-05-13 08:13:38 -03:00
|
|
|
for (size_t i = 0; i < async_scrolling_state.scroll_nodes.size(); ++i) {
|
|
|
|
|
auto parent_scroll_frame_index = parent_scroll_frame_indices[i];
|
|
|
|
|
if (parent_scroll_frame_index.value())
|
|
|
|
|
async_scrolling_state.scroll_nodes[i].parent_node_id = scroll_node_id_for(async_scrolling_state.scroll_nodes[i].node_id.document_id, parent_scroll_frame_index);
|
|
|
|
|
}
|
2026-05-14 07:22:26 -03:00
|
|
|
|
|
|
|
|
VERIFY(wheel_hit_test_target_scroll_frame_indices.size() == async_scrolling_state.wheel_hit_test_targets.size());
|
|
|
|
|
VERIFY(wheel_hit_test_target_document_ids.size() == async_scrolling_state.wheel_hit_test_targets.size());
|
|
|
|
|
for (size_t i = 0; i < async_scrolling_state.wheel_hit_test_targets.size(); ++i) {
|
|
|
|
|
auto target_scroll_frame_index = wheel_hit_test_target_scroll_frame_indices[i];
|
|
|
|
|
if (target_scroll_frame_index.value())
|
|
|
|
|
async_scrolling_state.wheel_hit_test_targets[i].target_node_id = scroll_node_id_for(wheel_hit_test_target_document_ids[i], target_scroll_frame_index);
|
|
|
|
|
}
|
LibWeb: Add compositor scroll state snapshots
Record the scroll geometry that CompositorThread can safely reason about
while the main thread is painting. The snapshot contains stable scroll
node IDs, parent links, scroll bounds, sticky inputs, and wheel blocker
regions in display-list coordinate space.
Add AsyncScrollingState and AsyncScrollTree under LibWeb/Compositor. The
state is the immutable main-thread snapshot; the tree is the mutable
compositor-side copy that can replay scroll deltas and sticky offsets
without touching DOM, layout, or paintables.
Expose the state through internals and add text tests for tree shape,
parent links, sticky areas, blocker hit testing, nested scrollers, and
admission decisions. Keep the directory skipped unless the feature is
enabled with --enable-async-scrolling.
2026-05-11 16:27:11 -03:00
|
|
|
return async_scrolling_state;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-11 18:51:57 -03:00
|
|
|
WheelRoutingAdmission wheel_routing_admission_for(AsyncScrollingState const& state)
|
|
|
|
|
{
|
2026-05-14 11:25:42 -03:00
|
|
|
if (state.has_blocking_wheel_event_region_covering_viewport)
|
2026-05-11 18:51:57 -03:00
|
|
|
return WheelRoutingAdmission::BlockingWheelEventListeners;
|
|
|
|
|
|
2026-05-14 11:25:42 -03:00
|
|
|
if (state.scroll_nodes.is_empty())
|
|
|
|
|
return WheelRoutingAdmission::NoScrollNode;
|
2026-05-11 18:51:57 -03:00
|
|
|
return WheelRoutingAdmission::Accepted;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
StringView wheel_routing_admission_to_string(WheelRoutingAdmission admission)
|
|
|
|
|
{
|
|
|
|
|
switch (admission) {
|
|
|
|
|
case WheelRoutingAdmission::Accepted:
|
|
|
|
|
return "accepted"sv;
|
|
|
|
|
case WheelRoutingAdmission::NoAsyncScrollingState:
|
|
|
|
|
return "no async scrolling state"sv;
|
|
|
|
|
case WheelRoutingAdmission::BlockingWheelEventListeners:
|
|
|
|
|
return "blocking wheel event listeners"sv;
|
2026-05-14 11:25:42 -03:00
|
|
|
case WheelRoutingAdmission::NoScrollNode:
|
|
|
|
|
return "no scroll node"sv;
|
2026-05-11 19:19:00 -03:00
|
|
|
case WheelRoutingAdmission::StaleWheelEventListeners:
|
|
|
|
|
return "stale wheel event listeners"sv;
|
2026-05-11 18:51:57 -03:00
|
|
|
}
|
|
|
|
|
VERIFY_NOT_REACHED();
|
|
|
|
|
}
|
|
|
|
|
|
LibWeb: Add compositor scroll state snapshots
Record the scroll geometry that CompositorThread can safely reason about
while the main thread is painting. The snapshot contains stable scroll
node IDs, parent links, scroll bounds, sticky inputs, and wheel blocker
regions in display-list coordinate space.
Add AsyncScrollingState and AsyncScrollTree under LibWeb/Compositor. The
state is the immutable main-thread snapshot; the tree is the mutable
compositor-side copy that can replay scroll deltas and sticky offsets
without touching DOM, layout, or paintables.
Expose the state through internals and add text tests for tree shape,
parent links, sticky areas, blocker hit testing, nested scrollers, and
admission decisions. Keep the directory skipped unless the feature is
enabled with --enable-async-scrolling.
2026-05-11 16:27:11 -03:00
|
|
|
bool blocks_wheel_event_at_position(AsyncScrollingState const& async_scrolling_state, RefPtr<Painting::DisplayList> const& display_list, Painting::ScrollStateSnapshot const& scroll_state_snapshot, Gfx::FloatPoint position)
|
|
|
|
|
{
|
|
|
|
|
if (async_scrolling_state.has_blocking_wheel_event_region_covering_viewport)
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
// If a caller knows blocking wheel listeners exist but cannot provide a display list for visual-context hit
|
|
|
|
|
// testing, async scrolling must fail closed. Sending the input to the main thread is slower, but it preserves
|
|
|
|
|
// cancelability.
|
|
|
|
|
if (!display_list)
|
|
|
|
|
return async_scrolling_state.has_blocking_wheel_event_listeners;
|
|
|
|
|
|
|
|
|
|
auto const& visual_context_tree = display_list->visual_context_tree();
|
|
|
|
|
for (auto const& region : async_scrolling_state.blocking_wheel_event_regions) {
|
|
|
|
|
auto position_in_context = visual_context_tree.transform_point_for_hit_test(region.visual_context_index, position, scroll_state_snapshot);
|
|
|
|
|
if (position_in_context.has_value() && region.rect.contains(*position_in_context))
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-13 08:13:38 -03:00
|
|
|
static WheelHitTestResult hit_test_scroll_node_at_position(AsyncScrollingState const& async_scrolling_state, RefPtr<Painting::DisplayList> const& display_list, Painting::ScrollStateSnapshot const& scroll_state_snapshot, Gfx::FloatPoint position, Gfx::FloatPoint delta)
|
2026-05-11 17:35:57 -03:00
|
|
|
{
|
|
|
|
|
if (!display_list)
|
2026-05-13 08:13:38 -03:00
|
|
|
return {};
|
LibWeb: Add compositor scroll state snapshots
Record the scroll geometry that CompositorThread can safely reason about
while the main thread is painting. The snapshot contains stable scroll
node IDs, parent links, scroll bounds, sticky inputs, and wheel blocker
regions in display-list coordinate space.
Add AsyncScrollingState and AsyncScrollTree under LibWeb/Compositor. The
state is the immutable main-thread snapshot; the tree is the mutable
compositor-side copy that can replay scroll deltas and sticky offsets
without touching DOM, layout, or paintables.
Expose the state through internals and add text tests for tree shape,
parent links, sticky areas, blocker hit testing, nested scrollers, and
admission decisions. Keep the directory skipped unless the feature is
enabled with --enable-async-scrolling.
2026-05-11 16:27:11 -03:00
|
|
|
|
2026-05-13 08:13:38 -03:00
|
|
|
AsyncScrollTree scroll_tree;
|
|
|
|
|
auto async_scrolling_state_copy = async_scrolling_state;
|
|
|
|
|
scroll_tree.set_state(move(async_scrolling_state_copy));
|
2026-05-14 07:22:26 -03:00
|
|
|
scroll_tree.rebuild_wheel_hit_test_targets(display_list, scroll_state_snapshot);
|
2026-05-13 08:13:38 -03:00
|
|
|
return scroll_tree.hit_test_scroll_node_for_wheel(position, delta);
|
LibWeb: Add compositor scroll state snapshots
Record the scroll geometry that CompositorThread can safely reason about
while the main thread is painting. The snapshot contains stable scroll
node IDs, parent links, scroll bounds, sticky inputs, and wheel blocker
regions in display-list coordinate space.
Add AsyncScrollingState and AsyncScrollTree under LibWeb/Compositor. The
state is the immutable main-thread snapshot; the tree is the mutable
compositor-side copy that can replay scroll deltas and sticky offsets
without touching DOM, layout, or paintables.
Expose the state through internals and add text tests for tree shape,
parent links, sticky areas, blocker hit testing, nested scrollers, and
admission decisions. Keep the directory skipped unless the feature is
enabled with --enable-async-scrolling.
2026-05-11 16:27:11 -03:00
|
|
|
}
|
|
|
|
|
|
2026-05-13 08:13:38 -03:00
|
|
|
WheelScrollAdmission admit_wheel_scroll(AsyncScrollingState const& async_scrolling_state, RefPtr<Painting::DisplayList> const& display_list, Painting::ScrollStateSnapshot const& scroll_state_snapshot, Gfx::FloatPoint position, Gfx::FloatPoint delta, bool blocking_wheel_event_regions_are_current)
|
LibWeb: Add compositor scroll state snapshots
Record the scroll geometry that CompositorThread can safely reason about
while the main thread is painting. The snapshot contains stable scroll
node IDs, parent links, scroll bounds, sticky inputs, and wheel blocker
regions in display-list coordinate space.
Add AsyncScrollingState and AsyncScrollTree under LibWeb/Compositor. The
state is the immutable main-thread snapshot; the tree is the mutable
compositor-side copy that can replay scroll deltas and sticky offsets
without touching DOM, layout, or paintables.
Expose the state through internals and add text tests for tree shape,
parent links, sticky areas, blocker hit testing, nested scrollers, and
admission decisions. Keep the directory skipped unless the feature is
enabled with --enable-async-scrolling.
2026-05-11 16:27:11 -03:00
|
|
|
{
|
2026-05-13 08:13:38 -03:00
|
|
|
auto hit_test_result = hit_test_scroll_node_at_position(async_scrolling_state, display_list, scroll_state_snapshot, position, delta);
|
|
|
|
|
if (hit_test_result.blocked_by_main_thread_region)
|
2026-05-11 17:35:57 -03:00
|
|
|
return WheelScrollAdmission::BlockedByMainThreadRegion;
|
|
|
|
|
|
LibWeb: Add compositor scroll state snapshots
Record the scroll geometry that CompositorThread can safely reason about
while the main thread is painting. The snapshot contains stable scroll
node IDs, parent links, scroll bounds, sticky inputs, and wheel blocker
regions in display-list coordinate space.
Add AsyncScrollingState and AsyncScrollTree under LibWeb/Compositor. The
state is the immutable main-thread snapshot; the tree is the mutable
compositor-side copy that can replay scroll deltas and sticky offsets
without touching DOM, layout, or paintables.
Expose the state through internals and add text tests for tree shape,
parent links, sticky areas, blocker hit testing, nested scrollers, and
admission decisions. Keep the directory skipped unless the feature is
enabled with --enable-async-scrolling.
2026-05-11 16:27:11 -03:00
|
|
|
// Async scrolling may only start when the snapshot can prove that the wheel event cannot be canceled by script
|
|
|
|
|
// at this position. Stale or missing blocker information sends the event back to the main thread.
|
2026-05-13 08:13:38 -03:00
|
|
|
if (async_scrolling_state.has_blocking_wheel_event_listeners) {
|
LibWeb: Add compositor scroll state snapshots
Record the scroll geometry that CompositorThread can safely reason about
while the main thread is painting. The snapshot contains stable scroll
node IDs, parent links, scroll bounds, sticky inputs, and wheel blocker
regions in display-list coordinate space.
Add AsyncScrollingState and AsyncScrollTree under LibWeb/Compositor. The
state is the immutable main-thread snapshot; the tree is the mutable
compositor-side copy that can replay scroll deltas and sticky offsets
without touching DOM, layout, or paintables.
Expose the state through internals and add text tests for tree shape,
parent links, sticky areas, blocker hit testing, nested scrollers, and
admission decisions. Keep the directory skipped unless the feature is
enabled with --enable-async-scrolling.
2026-05-11 16:27:11 -03:00
|
|
|
if (!blocking_wheel_event_regions_are_current)
|
|
|
|
|
return WheelScrollAdmission::StaleBlockingWheelEventRegions;
|
|
|
|
|
if (blocks_wheel_event_at_position(async_scrolling_state, display_list, scroll_state_snapshot, position))
|
|
|
|
|
return WheelScrollAdmission::BlockedByWheelEventRegion;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-13 08:13:38 -03:00
|
|
|
if (!hit_test_result.node_id.has_value())
|
LibWeb: Add compositor scroll state snapshots
Record the scroll geometry that CompositorThread can safely reason about
while the main thread is painting. The snapshot contains stable scroll
node IDs, parent links, scroll bounds, sticky inputs, and wheel blocker
regions in display-list coordinate space.
Add AsyncScrollingState and AsyncScrollTree under LibWeb/Compositor. The
state is the immutable main-thread snapshot; the tree is the mutable
compositor-side copy that can replay scroll deltas and sticky offsets
without touching DOM, layout, or paintables.
Expose the state through internals and add text tests for tree shape,
parent links, sticky areas, blocker hit testing, nested scrollers, and
admission decisions. Keep the directory skipped unless the feature is
enabled with --enable-async-scrolling.
2026-05-11 16:27:11 -03:00
|
|
|
return WheelScrollAdmission::NoScrollableTarget;
|
|
|
|
|
return WheelScrollAdmission::Accepted;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|