LibWeb: Move async scrolling recording to paintables
Async scrolling display-list items were still recorded by helper functions in LibWeb/Compositor even though they are produced while walking the paint tree. That kept paint-time knowledge about boxes, viewport state, and wheel-event regions in the compositor-facing code. Move viewport setup and final metadata emission to ViewportPaintable, and move per-box scroll-node, hit-test, scrollbar, wheel-region, and sticky-area item recording to PaintableBox. AsyncScrollingState now keeps the compositor-side conversion and lookup logic for consuming the recorded metadata.
This commit is contained in:
parent
6d70fa20bf
commit
c78dd59cc3
7 changed files with 305 additions and 309 deletions
|
|
@ -4,86 +4,18 @@
|
|||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibWeb/CSS/ComputedValues.h>
|
||||
#include <LibWeb/Compositor/AsyncScrollTree.h>
|
||||
#include <LibWeb/Compositor/AsyncScrollingState.h>
|
||||
#include <LibWeb/DOM/DOMEventListener.h>
|
||||
#include <LibWeb/DOM/Document.h>
|
||||
#include <LibWeb/DOM/Element.h>
|
||||
#include <LibWeb/DOM/EventTarget.h>
|
||||
#include <LibWeb/HTML/Navigable.h>
|
||||
#include <LibWeb/HTML/NavigableContainer.h>
|
||||
#include <LibWeb/HTML/Window.h>
|
||||
#include <LibWeb/Page/Page.h>
|
||||
#include <LibWeb/Painting/DisplayList.h>
|
||||
#include <LibWeb/Painting/DisplayListRecorder.h>
|
||||
#include <LibWeb/Painting/DisplayListRecordingContext.h>
|
||||
#include <LibWeb/Painting/PaintableBox.h>
|
||||
#include <LibWeb/Painting/ScrollState.h>
|
||||
#include <LibWeb/Painting/ViewportPaintable.h>
|
||||
#include <LibWeb/PixelUnits.h>
|
||||
|
||||
#include <AK/StdLibExtras.h>
|
||||
|
||||
namespace Web::Compositor {
|
||||
|
||||
static Gfx::FloatPoint css_point_to_device_point(CSSPixelPoint point, double device_pixels_per_css_pixel)
|
||||
{
|
||||
auto scale = static_cast<float>(device_pixels_per_css_pixel);
|
||||
return { point.x().to_float() * scale, point.y().to_float() * scale };
|
||||
}
|
||||
|
||||
static Gfx::FloatSize css_size_to_device_size(CSSPixelSize size, double device_pixels_per_css_pixel)
|
||||
{
|
||||
auto scale = static_cast<float>(device_pixels_per_css_pixel);
|
||||
return { size.width().to_float() * scale, size.height().to_float() * scale };
|
||||
}
|
||||
|
||||
static Gfx::FloatRect css_rect_to_device_rect(CSSPixelRect rect, double device_pixels_per_css_pixel)
|
||||
{
|
||||
return { css_point_to_device_point(rect.location(), device_pixels_per_css_pixel), css_size_to_device_size(rect.size(), device_pixels_per_css_pixel) };
|
||||
}
|
||||
|
||||
static Optional<float> css_inset_to_device_inset(Optional<CSSPixels> inset, double device_pixels_per_css_pixel)
|
||||
{
|
||||
if (!inset.has_value())
|
||||
return {};
|
||||
return inset->to_float() * static_cast<float>(device_pixels_per_css_pixel);
|
||||
}
|
||||
|
||||
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 };
|
||||
}
|
||||
|
||||
static Optional<Painting::CompositorScrollNodeKind> scroll_node_kind_for(Painting::PaintableBox const& paintable_box)
|
||||
{
|
||||
if (paintable_box.is_viewport_paintable())
|
||||
return Painting::CompositorScrollNodeKind::Viewport;
|
||||
if (paintable_box.layout_node().generated_for_pseudo_element().has_value())
|
||||
return Painting::CompositorScrollNodeKind::PseudoElement;
|
||||
if (paintable_box.dom_node() && is<DOM::Element>(*paintable_box.dom_node()))
|
||||
return Painting::CompositorScrollNodeKind::Element;
|
||||
return {};
|
||||
}
|
||||
|
||||
static UniqueNodeID scrollable_node_id_for(Painting::PaintableBox const& paintable_box)
|
||||
{
|
||||
if (paintable_box.is_viewport_paintable())
|
||||
return paintable_box.document().unique_id();
|
||||
if (paintable_box.layout_node().generated_for_pseudo_element().has_value())
|
||||
return paintable_box.layout_node().pseudo_element_generator()->unique_id();
|
||||
return paintable_box.dom_node()->unique_id();
|
||||
}
|
||||
|
||||
static u8 pseudo_element_type_for(Painting::PaintableBox const& paintable_box)
|
||||
{
|
||||
auto pseudo_element = paintable_box.layout_node().generated_for_pseudo_element();
|
||||
if (!pseudo_element.has_value())
|
||||
return 0;
|
||||
return static_cast<u8>(to_underlying(*pseudo_element));
|
||||
}
|
||||
|
||||
static AsyncScrollNodeKind async_scroll_node_kind_for(Painting::CompositorScrollNodeKind kind)
|
||||
{
|
||||
switch (kind) {
|
||||
|
|
@ -106,239 +38,6 @@ static AsyncScrollNodeStableID stable_scroll_node_id_for(UniqueNodeID scrollable
|
|||
};
|
||||
}
|
||||
|
||||
static bool is_nested_navigable_container(Painting::PaintableBox const& paintable_box)
|
||||
{
|
||||
auto node = paintable_box.dom_node();
|
||||
return node && node->is_navigable_container() && as<HTML::NavigableContainer const>(*node).content_navigable();
|
||||
}
|
||||
|
||||
static CSSPixelPoint maximum_scroll_offset_for(Painting::PaintableBox const& paintable_box)
|
||||
{
|
||||
CSSPixelPoint max_scroll_offset;
|
||||
auto scrollable_overflow_rect = paintable_box.scrollable_overflow_rect();
|
||||
if (!scrollable_overflow_rect.has_value())
|
||||
return max_scroll_offset;
|
||||
|
||||
auto scrollport_rect = paintable_box.absolute_padding_box_rect();
|
||||
|
||||
max_scroll_offset.set_x(max(CSSPixels(0), scrollable_overflow_rect->width() - scrollport_rect.width()));
|
||||
max_scroll_offset.set_y(max(CSSPixels(0), scrollable_overflow_rect->height() - scrollport_rect.height()));
|
||||
return max_scroll_offset;
|
||||
}
|
||||
|
||||
static void record_scroll_node(Painting::PaintableBox const& paintable_box, DisplayListRecordingContext& context)
|
||||
{
|
||||
auto scroll_node_kind = scroll_node_kind_for(paintable_box);
|
||||
if (!scroll_node_kind.has_value())
|
||||
return;
|
||||
|
||||
auto parent_scroll_frame_index = Painting::ScrollFrameIndex {};
|
||||
if (auto scrollable_ancestor = paintable_box.nearest_scrollable_ancestor())
|
||||
parent_scroll_frame_index = scrollable_ancestor->own_scroll_frame_index();
|
||||
|
||||
auto scrollport_rect = paintable_box.is_viewport_paintable()
|
||||
? Gfx::IntRect { {}, context.device_viewport_rect().size().to_type<int>() }
|
||||
: context.rounded_device_rect(paintable_box.absolute_padding_box_rect()).to_type<int>();
|
||||
|
||||
auto& recorder = context.display_list_recorder();
|
||||
recorder.compositor_scroll_node({
|
||||
.document_id = paintable_box.document().unique_id(),
|
||||
.scrollable_node_id = scrollable_node_id_for(paintable_box),
|
||||
.scroll_frame_index = paintable_box.own_scroll_frame_index(),
|
||||
.parent_scroll_frame_index = parent_scroll_frame_index,
|
||||
.scrollport_rect = scrollport_rect,
|
||||
.max_scroll_offset = css_point_to_device_point(maximum_scroll_offset_for(paintable_box), context.device_pixels_per_css_pixel()),
|
||||
.scroll_node_kind = *scroll_node_kind,
|
||||
.pseudo_element_type = pseudo_element_type_for(paintable_box),
|
||||
.is_viewport = paintable_box.is_viewport_paintable(),
|
||||
.can_be_wheel_scrolled_horizontally = paintable_box.could_be_scrolled_by_wheel_event(Painting::PaintableBox::ScrollDirection::Horizontal),
|
||||
.can_be_wheel_scrolled_vertically = paintable_box.could_be_scrolled_by_wheel_event(Painting::PaintableBox::ScrollDirection::Vertical),
|
||||
});
|
||||
}
|
||||
|
||||
static void record_main_thread_wheel_event_region(Painting::PaintableBox const& paintable_box, DisplayListRecordingContext& context)
|
||||
{
|
||||
auto rect = css_rect_to_device_rect(paintable_box.absolute_united_border_box_rect(), context.device_pixels_per_css_pixel());
|
||||
if (rect.is_empty())
|
||||
return;
|
||||
|
||||
context.display_list_recorder().compositor_main_thread_wheel_event_region({
|
||||
.rect = rect,
|
||||
});
|
||||
}
|
||||
|
||||
static Optional<Painting::ScrollFrameIndex> wheel_hit_test_target_scroll_frame_index_for(Painting::PaintableBox const& paintable_box)
|
||||
{
|
||||
if (paintable_box.own_scroll_frame_index().value() && paintable_box.could_be_scrolled_by_wheel_event())
|
||||
return paintable_box.own_scroll_frame_index();
|
||||
if (auto scrollable_ancestor = paintable_box.nearest_scrollable_ancestor())
|
||||
return scrollable_ancestor->own_scroll_frame_index();
|
||||
if (auto viewport_paintable = paintable_box.document().paintable(); viewport_paintable && viewport_paintable->could_be_scrolled_by_wheel_event())
|
||||
return viewport_paintable->own_scroll_frame_index();
|
||||
return {};
|
||||
}
|
||||
|
||||
static void record_wheel_hit_test_target(Painting::PaintableBox const& paintable_box, DisplayListRecordingContext& context)
|
||||
{
|
||||
if (!paintable_box.is_visible() || !paintable_box.visible_for_hit_testing())
|
||||
return;
|
||||
|
||||
auto rect = css_rect_to_device_rect(paintable_box.absolute_border_box_rect(), context.device_pixels_per_css_pixel());
|
||||
if (rect.is_empty())
|
||||
return;
|
||||
|
||||
context.display_list_recorder().compositor_wheel_hit_test_target({
|
||||
.document_id = paintable_box.document().unique_id(),
|
||||
.target_scroll_frame_index = wheel_hit_test_target_scroll_frame_index_for(paintable_box).value_or({}),
|
||||
.rect = rect,
|
||||
.corner_radii = paintable_box.border_radii_data().as_corners(context.device_pixel_converter()),
|
||||
});
|
||||
}
|
||||
|
||||
static void record_viewport_scrollbar_state(Painting::PaintableBox const& paintable_box, DisplayListRecordingContext& context)
|
||||
{
|
||||
if (!paintable_box.is_viewport_paintable())
|
||||
return;
|
||||
if (!paintable_box.document().page().async_scrolling_enabled())
|
||||
return;
|
||||
if (!Painting::should_paint_viewport_scrollbars())
|
||||
return;
|
||||
if (paintable_box.computed_values().scrollbar_width() == CSS::ScrollbarWidth::None)
|
||||
return;
|
||||
|
||||
auto scrollbar_colors = paintable_box.computed_values().scrollbar_color();
|
||||
auto const& metrics = context.chrome_metrics();
|
||||
|
||||
for (auto direction : { Painting::PaintableBox::ScrollDirection::Vertical, Painting::PaintableBox::ScrollDirection::Horizontal }) {
|
||||
auto scrollbar_data = paintable_box.compute_scrollbar_data(direction, metrics, nullptr, Painting::PaintableBox::ScrollbarSizing::Regular);
|
||||
if (!scrollbar_data.has_value())
|
||||
continue;
|
||||
auto expanded_scrollbar_data = paintable_box.compute_scrollbar_data(direction, metrics, nullptr, Painting::PaintableBox::ScrollbarSizing::Enlarged);
|
||||
VERIFY(expanded_scrollbar_data.has_value());
|
||||
|
||||
auto gutter_rect = context.rounded_device_rect(scrollbar_data->gutter_rect).to_type<int>();
|
||||
auto max_scroll_offset = css_point_to_device_point(maximum_scroll_offset_for(paintable_box), context.device_pixels_per_css_pixel());
|
||||
auto orientation = direction == Painting::PaintableBox::ScrollDirection::Horizontal ? Gfx::Orientation::Horizontal : Gfx::Orientation::Vertical;
|
||||
auto thumb_color = scrollbar_colors.thumb_color;
|
||||
if (gutter_rect.is_empty() && thumb_color == CSS::InitialValues::scrollbar_color().thumb_color)
|
||||
thumb_color = thumb_color.with_alpha(128);
|
||||
|
||||
context.display_list_recorder().compositor_viewport_scrollbar({
|
||||
.document_id = paintable_box.document().unique_id(),
|
||||
.scroll_frame_index = paintable_box.own_scroll_frame_index(),
|
||||
.gutter_rect = gutter_rect,
|
||||
.thumb_rect = context.rounded_device_rect(scrollbar_data->thumb_rect).to_type<int>(),
|
||||
.expanded_gutter_rect = context.rounded_device_rect(expanded_scrollbar_data->gutter_rect).to_type<int>(),
|
||||
.expanded_thumb_rect = context.rounded_device_rect(expanded_scrollbar_data->thumb_rect).to_type<int>(),
|
||||
.scroll_size = scrollbar_data->thumb_travel_to_scroll_ratio.to_double(),
|
||||
.expanded_scroll_size = expanded_scrollbar_data->thumb_travel_to_scroll_ratio.to_double(),
|
||||
.max_scroll_offset = max_scroll_offset.primary_offset_for_orientation(orientation),
|
||||
.thumb_color = thumb_color,
|
||||
.track_color = scrollbar_colors.track_color,
|
||||
.vertical = direction == Painting::PaintableBox::ScrollDirection::Vertical,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
static bool is_root_wheel_event_target(DOM::Node const& node)
|
||||
{
|
||||
auto& document = node.document();
|
||||
return &node == document.document_element() || &node == document.body();
|
||||
}
|
||||
|
||||
static void collect_root_blocking_wheel_event_regions(AsyncScrollingState& async_scrolling_state, DOM::Document& document)
|
||||
{
|
||||
DOM::EventTarget* roots[] = {
|
||||
document.navigable() ? document.navigable()->active_window() : nullptr,
|
||||
&document,
|
||||
document.document_element(),
|
||||
document.body(),
|
||||
};
|
||||
for (auto* target : roots) {
|
||||
if (target && target->has_blocking_wheel_event_listener()) {
|
||||
async_scrolling_state.has_blocking_wheel_event_listeners = true;
|
||||
async_scrolling_state.has_blocking_wheel_event_region_covering_viewport = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void initialize_async_scrolling_metadata_recording(DisplayListRecordingContext& context, Painting::ViewportPaintable& document_paintable)
|
||||
{
|
||||
AsyncScrollingState async_scrolling_state;
|
||||
collect_root_blocking_wheel_event_regions(async_scrolling_state, document_paintable.document());
|
||||
context.set_async_scrolling_metadata_context(
|
||||
document_paintable.document().unique_id(),
|
||||
document_paintable.scroll_state(),
|
||||
async_scrolling_state.has_blocking_wheel_event_listeners,
|
||||
async_scrolling_state.has_blocking_wheel_event_region_covering_viewport);
|
||||
}
|
||||
|
||||
void record_async_scrolling_metadata_for_paintable(Painting::PaintableBox const& paintable_box, DisplayListRecordingContext& context)
|
||||
{
|
||||
if (!context.is_recording_async_scrolling_metadata())
|
||||
return;
|
||||
|
||||
auto device_pixels_per_css_pixel = context.device_pixels_per_css_pixel();
|
||||
auto& recorder = context.display_list_recorder();
|
||||
|
||||
record_wheel_hit_test_target(paintable_box, context);
|
||||
|
||||
if (!context.has_blocking_wheel_event_region_covering_viewport()) {
|
||||
if (auto node = paintable_box.dom_node(); node && !is_root_wheel_event_target(*node) && node->has_blocking_wheel_event_listener()) {
|
||||
context.set_has_blocking_wheel_event_listeners(true);
|
||||
recorder.compositor_blocking_wheel_event_region({
|
||||
.rect = css_rect_to_device_rect(paintable_box.absolute_united_border_box_rect(), device_pixels_per_css_pixel),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (is_nested_navigable_container(paintable_box)) {
|
||||
record_main_thread_wheel_event_region(paintable_box, context);
|
||||
} else if (paintable_box.own_scroll_frame_index().value() && paintable_box.could_be_scrolled_by_wheel_event()) {
|
||||
record_scroll_node(paintable_box, context);
|
||||
}
|
||||
record_viewport_scrollbar_state(paintable_box, context);
|
||||
|
||||
auto const& scroll_state = context.async_scrolling_scroll_state();
|
||||
auto sticky_frame_index = paintable_box.enclosing_scroll_frame_index();
|
||||
if (paintable_box.is_sticky_position() && sticky_frame_index.value()) {
|
||||
auto const& frame = scroll_state.frame_at(sticky_frame_index);
|
||||
if (frame.is_sticky() && frame.has_sticky_constraints()) {
|
||||
auto const& constraints = frame.sticky_constraints();
|
||||
auto const& insets = constraints.insets;
|
||||
recorder.compositor_sticky_area({
|
||||
.document_id = context.async_scrolling_document_id(),
|
||||
.scroll_frame_index = sticky_frame_index,
|
||||
.parent_scroll_frame_index = frame.parent_index(),
|
||||
.nearest_scrolling_ancestor_index = scroll_state.nearest_scrolling_ancestor(sticky_frame_index),
|
||||
.position_relative_to_scroll_ancestor = css_point_to_device_point(constraints.position_relative_to_scroll_ancestor, device_pixels_per_css_pixel),
|
||||
.border_box_size = css_size_to_device_size(constraints.border_box_size, device_pixels_per_css_pixel),
|
||||
.scrollport_size = css_size_to_device_size(constraints.scrollport_size, device_pixels_per_css_pixel),
|
||||
.containing_block_region = css_rect_to_device_rect(constraints.containing_block_region, device_pixels_per_css_pixel),
|
||||
.needs_parent_offset_adjustment = constraints.needs_parent_offset_adjustment,
|
||||
.inset_top = css_inset_to_device_inset(insets.top, device_pixels_per_css_pixel),
|
||||
.inset_right = css_inset_to_device_inset(insets.right, device_pixels_per_css_pixel),
|
||||
.inset_bottom = css_inset_to_device_inset(insets.bottom, device_pixels_per_css_pixel),
|
||||
.inset_left = css_inset_to_device_inset(insets.left, device_pixels_per_css_pixel),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void finalize_async_scrolling_metadata_recording(DisplayListRecordingContext& context, HTML::Navigable& navigable, Gfx::IntRect viewport_rect)
|
||||
{
|
||||
if (!context.is_recording_async_scrolling_metadata())
|
||||
return;
|
||||
|
||||
context.display_list_recorder().set_async_scrolling_metadata({
|
||||
.viewport_rect = viewport_rect,
|
||||
.wheel_event_listener_state_generation = navigable.page().wheel_event_listener_state_generation(),
|
||||
.has_blocking_wheel_event_listeners = context.has_blocking_wheel_event_listeners(),
|
||||
.has_blocking_wheel_event_region_covering_viewport = context.has_blocking_wheel_event_region_covering_viewport(),
|
||||
});
|
||||
}
|
||||
|
||||
AsyncScrollingState async_scrolling_state_from_display_list(Painting::DisplayList const& display_list)
|
||||
{
|
||||
AsyncScrollingState async_scrolling_state;
|
||||
|
|
|
|||
|
|
@ -155,9 +155,6 @@ enum class WheelScrollAdmission {
|
|||
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);
|
||||
|
|
|
|||
|
|
@ -74,7 +74,6 @@
|
|||
#include <LibWeb/CSS/SystemColor.h>
|
||||
#include <LibWeb/CSS/TransitionEvent.h>
|
||||
#include <LibWeb/CSS/VisualViewport.h>
|
||||
#include <LibWeb/Compositor/AsyncScrollingState.h>
|
||||
#include <LibWeb/ContentSecurityPolicy/Directives/Directive.h>
|
||||
#include <LibWeb/ContentSecurityPolicy/Policy.h>
|
||||
#include <LibWeb/ContentSecurityPolicy/PolicyList.h>
|
||||
|
|
@ -8233,10 +8232,10 @@ RefPtr<Painting::DisplayList> Document::record_display_list(HTML::PaintConfig co
|
|||
|
||||
auto& viewport_paintable = *paintable();
|
||||
viewport_paintable.refresh_scroll_state();
|
||||
Compositor::initialize_async_scrolling_metadata_recording(context, viewport_paintable);
|
||||
viewport_paintable.initialize_async_scrolling_metadata_recording(context);
|
||||
|
||||
viewport_paintable.paint_all_phases(context);
|
||||
Compositor::finalize_async_scrolling_metadata_recording(context, *navigable(), viewport_rect.to_type<int>());
|
||||
viewport_paintable.finalize_async_scrolling_metadata_recording(context, *navigable(), viewport_rect.to_type<int>());
|
||||
|
||||
if (highlighted_node() && highlighted_node()->paintable()) {
|
||||
highlighted_node()->paintable()->paint_inspector_overlay(context);
|
||||
|
|
|
|||
|
|
@ -8,18 +8,20 @@
|
|||
*/
|
||||
|
||||
#include <AK/GenericShorthands.h>
|
||||
#include <AK/StdLibExtras.h>
|
||||
#include <LibGfx/Font/Font.h>
|
||||
#include <LibWeb/CSS/ComputedProperties.h>
|
||||
#include <LibWeb/CSS/ComputedValues.h>
|
||||
#include <LibWeb/CSS/StyleScope.h>
|
||||
#include <LibWeb/CSS/StyleValues/FilterValueListStyleValue.h>
|
||||
#include <LibWeb/Compositor/AsyncScrollingState.h>
|
||||
#include <LibWeb/DOM/Document.h>
|
||||
#include <LibWeb/DOM/Element.h>
|
||||
#include <LibWeb/DOM/EventTarget.h>
|
||||
#include <LibWeb/DOM/ShadowRoot.h>
|
||||
#include <LibWeb/HTML/HTMLBodyElement.h>
|
||||
#include <LibWeb/HTML/HTMLHtmlElement.h>
|
||||
#include <LibWeb/HTML/Navigable.h>
|
||||
#include <LibWeb/HTML/NavigableContainer.h>
|
||||
#include <LibWeb/Layout/InlineNode.h>
|
||||
#include <LibWeb/Page/EventHandler.h>
|
||||
#include <LibWeb/Page/MiddleButtonScrollHandler.h>
|
||||
|
|
@ -28,6 +30,7 @@
|
|||
#include <LibWeb/Painting/BorderRadiusCornerClipper.h>
|
||||
#include <LibWeb/Painting/ChromeMetrics.h>
|
||||
#include <LibWeb/Painting/DisplayListRecorder.h>
|
||||
#include <LibWeb/Painting/DisplayListRecordingContext.h>
|
||||
#include <LibWeb/Painting/PaintableBox.h>
|
||||
#include <LibWeb/Painting/ResizeHandle.h>
|
||||
#include <LibWeb/Painting/SVGPaintable.h>
|
||||
|
|
@ -85,6 +88,198 @@ bool should_paint_viewport_scrollbars()
|
|||
return g_paint_viewport_scrollbars;
|
||||
}
|
||||
|
||||
static Gfx::FloatPoint css_point_to_device_point(CSSPixelPoint point, double device_pixels_per_css_pixel)
|
||||
{
|
||||
auto scale = static_cast<float>(device_pixels_per_css_pixel);
|
||||
return { point.x().to_float() * scale, point.y().to_float() * scale };
|
||||
}
|
||||
|
||||
static Gfx::FloatSize css_size_to_device_size(CSSPixelSize size, double device_pixels_per_css_pixel)
|
||||
{
|
||||
auto scale = static_cast<float>(device_pixels_per_css_pixel);
|
||||
return { size.width().to_float() * scale, size.height().to_float() * scale };
|
||||
}
|
||||
|
||||
static Gfx::FloatRect css_rect_to_device_rect(CSSPixelRect rect, double device_pixels_per_css_pixel)
|
||||
{
|
||||
return { css_point_to_device_point(rect.location(), device_pixels_per_css_pixel), css_size_to_device_size(rect.size(), device_pixels_per_css_pixel) };
|
||||
}
|
||||
|
||||
static Optional<float> css_inset_to_device_inset(Optional<CSSPixels> inset, double device_pixels_per_css_pixel)
|
||||
{
|
||||
if (!inset.has_value())
|
||||
return {};
|
||||
return inset->to_float() * static_cast<float>(device_pixels_per_css_pixel);
|
||||
}
|
||||
|
||||
static Optional<CompositorScrollNodeKind> scroll_node_kind_for(PaintableBox const& paintable_box)
|
||||
{
|
||||
if (paintable_box.is_viewport_paintable())
|
||||
return CompositorScrollNodeKind::Viewport;
|
||||
if (paintable_box.layout_node().generated_for_pseudo_element().has_value())
|
||||
return CompositorScrollNodeKind::PseudoElement;
|
||||
if (paintable_box.dom_node() && is<DOM::Element>(*paintable_box.dom_node()))
|
||||
return CompositorScrollNodeKind::Element;
|
||||
return {};
|
||||
}
|
||||
|
||||
static UniqueNodeID scrollable_node_id_for(PaintableBox const& paintable_box)
|
||||
{
|
||||
if (paintable_box.is_viewport_paintable())
|
||||
return paintable_box.document().unique_id();
|
||||
if (paintable_box.layout_node().generated_for_pseudo_element().has_value())
|
||||
return paintable_box.layout_node().pseudo_element_generator()->unique_id();
|
||||
return paintable_box.dom_node()->unique_id();
|
||||
}
|
||||
|
||||
static u8 pseudo_element_type_for(PaintableBox const& paintable_box)
|
||||
{
|
||||
auto pseudo_element = paintable_box.layout_node().generated_for_pseudo_element();
|
||||
if (!pseudo_element.has_value())
|
||||
return 0;
|
||||
return static_cast<u8>(to_underlying(*pseudo_element));
|
||||
}
|
||||
|
||||
static bool is_nested_navigable_container(PaintableBox const& paintable_box)
|
||||
{
|
||||
auto node = paintable_box.dom_node();
|
||||
return node && node->is_navigable_container() && as<HTML::NavigableContainer const>(*node).content_navigable();
|
||||
}
|
||||
|
||||
static CSSPixelPoint maximum_scroll_offset_for(PaintableBox const& paintable_box)
|
||||
{
|
||||
CSSPixelPoint max_scroll_offset;
|
||||
auto scrollable_overflow_rect = paintable_box.scrollable_overflow_rect();
|
||||
if (!scrollable_overflow_rect.has_value())
|
||||
return max_scroll_offset;
|
||||
|
||||
auto scrollport_rect = paintable_box.absolute_padding_box_rect();
|
||||
|
||||
max_scroll_offset.set_x(max(CSSPixels(0), scrollable_overflow_rect->width() - scrollport_rect.width()));
|
||||
max_scroll_offset.set_y(max(CSSPixels(0), scrollable_overflow_rect->height() - scrollport_rect.height()));
|
||||
return max_scroll_offset;
|
||||
}
|
||||
|
||||
static void record_scroll_node(PaintableBox const& paintable_box, DisplayListRecordingContext& context)
|
||||
{
|
||||
auto scroll_node_kind = scroll_node_kind_for(paintable_box);
|
||||
if (!scroll_node_kind.has_value())
|
||||
return;
|
||||
|
||||
auto parent_scroll_frame_index = ScrollFrameIndex {};
|
||||
if (auto scrollable_ancestor = paintable_box.nearest_scrollable_ancestor())
|
||||
parent_scroll_frame_index = scrollable_ancestor->own_scroll_frame_index();
|
||||
|
||||
auto scrollport_rect = paintable_box.is_viewport_paintable()
|
||||
? Gfx::IntRect { {}, context.device_viewport_rect().size().to_type<int>() }
|
||||
: context.rounded_device_rect(paintable_box.absolute_padding_box_rect()).to_type<int>();
|
||||
|
||||
auto& recorder = context.display_list_recorder();
|
||||
recorder.compositor_scroll_node({
|
||||
.document_id = paintable_box.document().unique_id(),
|
||||
.scrollable_node_id = scrollable_node_id_for(paintable_box),
|
||||
.scroll_frame_index = paintable_box.own_scroll_frame_index(),
|
||||
.parent_scroll_frame_index = parent_scroll_frame_index,
|
||||
.scrollport_rect = scrollport_rect,
|
||||
.max_scroll_offset = css_point_to_device_point(maximum_scroll_offset_for(paintable_box), context.device_pixels_per_css_pixel()),
|
||||
.scroll_node_kind = *scroll_node_kind,
|
||||
.pseudo_element_type = pseudo_element_type_for(paintable_box),
|
||||
.is_viewport = paintable_box.is_viewport_paintable(),
|
||||
.can_be_wheel_scrolled_horizontally = paintable_box.could_be_scrolled_by_wheel_event(PaintableBox::ScrollDirection::Horizontal),
|
||||
.can_be_wheel_scrolled_vertically = paintable_box.could_be_scrolled_by_wheel_event(PaintableBox::ScrollDirection::Vertical),
|
||||
});
|
||||
}
|
||||
|
||||
static void record_main_thread_wheel_event_region(PaintableBox const& paintable_box, DisplayListRecordingContext& context)
|
||||
{
|
||||
auto rect = css_rect_to_device_rect(paintable_box.absolute_united_border_box_rect(), context.device_pixels_per_css_pixel());
|
||||
if (rect.is_empty())
|
||||
return;
|
||||
|
||||
context.display_list_recorder().compositor_main_thread_wheel_event_region({
|
||||
.rect = rect,
|
||||
});
|
||||
}
|
||||
|
||||
static Optional<ScrollFrameIndex> wheel_hit_test_target_scroll_frame_index_for(PaintableBox const& paintable_box)
|
||||
{
|
||||
if (paintable_box.own_scroll_frame_index().value() && paintable_box.could_be_scrolled_by_wheel_event())
|
||||
return paintable_box.own_scroll_frame_index();
|
||||
if (auto scrollable_ancestor = paintable_box.nearest_scrollable_ancestor())
|
||||
return scrollable_ancestor->own_scroll_frame_index();
|
||||
if (auto viewport_paintable = paintable_box.document().paintable(); viewport_paintable && viewport_paintable->could_be_scrolled_by_wheel_event())
|
||||
return viewport_paintable->own_scroll_frame_index();
|
||||
return {};
|
||||
}
|
||||
|
||||
static void record_wheel_hit_test_target(PaintableBox const& paintable_box, DisplayListRecordingContext& context)
|
||||
{
|
||||
if (!paintable_box.is_visible() || !paintable_box.visible_for_hit_testing())
|
||||
return;
|
||||
|
||||
auto rect = css_rect_to_device_rect(paintable_box.absolute_border_box_rect(), context.device_pixels_per_css_pixel());
|
||||
if (rect.is_empty())
|
||||
return;
|
||||
|
||||
context.display_list_recorder().compositor_wheel_hit_test_target({
|
||||
.document_id = paintable_box.document().unique_id(),
|
||||
.target_scroll_frame_index = wheel_hit_test_target_scroll_frame_index_for(paintable_box).value_or({}),
|
||||
.rect = rect,
|
||||
.corner_radii = paintable_box.border_radii_data().as_corners(context.device_pixel_converter()),
|
||||
});
|
||||
}
|
||||
|
||||
static void record_viewport_scrollbar_state(PaintableBox const& paintable_box, DisplayListRecordingContext& context)
|
||||
{
|
||||
if (!paintable_box.is_viewport_paintable())
|
||||
return;
|
||||
if (!paintable_box.document().page().async_scrolling_enabled())
|
||||
return;
|
||||
if (!should_paint_viewport_scrollbars())
|
||||
return;
|
||||
if (paintable_box.computed_values().scrollbar_width() == CSS::ScrollbarWidth::None)
|
||||
return;
|
||||
|
||||
auto scrollbar_colors = paintable_box.computed_values().scrollbar_color();
|
||||
auto const& metrics = context.chrome_metrics();
|
||||
|
||||
for (auto direction : { PaintableBox::ScrollDirection::Vertical, PaintableBox::ScrollDirection::Horizontal }) {
|
||||
auto scrollbar_data = paintable_box.compute_scrollbar_data(direction, metrics, nullptr, PaintableBox::ScrollbarSizing::Regular);
|
||||
if (!scrollbar_data.has_value())
|
||||
continue;
|
||||
auto expanded_scrollbar_data = paintable_box.compute_scrollbar_data(direction, metrics, nullptr, PaintableBox::ScrollbarSizing::Enlarged);
|
||||
VERIFY(expanded_scrollbar_data.has_value());
|
||||
|
||||
auto gutter_rect = context.rounded_device_rect(scrollbar_data->gutter_rect).to_type<int>();
|
||||
auto max_scroll_offset = css_point_to_device_point(maximum_scroll_offset_for(paintable_box), context.device_pixels_per_css_pixel());
|
||||
auto orientation = direction == PaintableBox::ScrollDirection::Horizontal ? Gfx::Orientation::Horizontal : Gfx::Orientation::Vertical;
|
||||
auto thumb_color = scrollbar_colors.thumb_color;
|
||||
if (gutter_rect.is_empty() && thumb_color == CSS::InitialValues::scrollbar_color().thumb_color)
|
||||
thumb_color = thumb_color.with_alpha(128);
|
||||
|
||||
context.display_list_recorder().compositor_viewport_scrollbar({
|
||||
.document_id = paintable_box.document().unique_id(),
|
||||
.scroll_frame_index = paintable_box.own_scroll_frame_index(),
|
||||
.gutter_rect = gutter_rect,
|
||||
.thumb_rect = context.rounded_device_rect(scrollbar_data->thumb_rect).to_type<int>(),
|
||||
.expanded_gutter_rect = context.rounded_device_rect(expanded_scrollbar_data->gutter_rect).to_type<int>(),
|
||||
.expanded_thumb_rect = context.rounded_device_rect(expanded_scrollbar_data->thumb_rect).to_type<int>(),
|
||||
.scroll_size = scrollbar_data->thumb_travel_to_scroll_ratio.to_double(),
|
||||
.expanded_scroll_size = expanded_scrollbar_data->thumb_travel_to_scroll_ratio.to_double(),
|
||||
.max_scroll_offset = max_scroll_offset.primary_offset_for_orientation(orientation),
|
||||
.thumb_color = thumb_color,
|
||||
.track_color = scrollbar_colors.track_color,
|
||||
.vertical = direction == PaintableBox::ScrollDirection::Vertical,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
static bool is_root_wheel_event_target(DOM::Node const& node)
|
||||
{
|
||||
auto& document = node.document();
|
||||
return &node == document.document_element() || &node == document.body();
|
||||
}
|
||||
|
||||
ResolvedCSSFilter resolve_css_filter(CSS::Filter const& computed_filter, PaintableBox const& paintable_box)
|
||||
{
|
||||
auto const& computed_values = paintable_box.computed_values();
|
||||
|
|
@ -740,10 +935,62 @@ Optional<PaintableBox::ScrollbarData> PaintableBox::compute_scrollbar_data(Scrol
|
|||
return scrollbar_data;
|
||||
}
|
||||
|
||||
void PaintableBox::record_async_scrolling_metadata(DisplayListRecordingContext& context) const
|
||||
{
|
||||
if (!context.is_recording_async_scrolling_metadata())
|
||||
return;
|
||||
|
||||
auto device_pixels_per_css_pixel = context.device_pixels_per_css_pixel();
|
||||
auto& recorder = context.display_list_recorder();
|
||||
|
||||
record_wheel_hit_test_target(*this, context);
|
||||
|
||||
if (!context.has_blocking_wheel_event_region_covering_viewport()) {
|
||||
if (auto node = dom_node(); node && !is_root_wheel_event_target(*node) && node->has_blocking_wheel_event_listener()) {
|
||||
context.set_has_blocking_wheel_event_listeners(true);
|
||||
recorder.compositor_blocking_wheel_event_region({
|
||||
.rect = css_rect_to_device_rect(absolute_united_border_box_rect(), device_pixels_per_css_pixel),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (is_nested_navigable_container(*this)) {
|
||||
record_main_thread_wheel_event_region(*this, context);
|
||||
} else if (own_scroll_frame_index().value() && could_be_scrolled_by_wheel_event()) {
|
||||
record_scroll_node(*this, context);
|
||||
}
|
||||
record_viewport_scrollbar_state(*this, context);
|
||||
|
||||
auto const& scroll_state = context.async_scrolling_scroll_state();
|
||||
auto sticky_frame_index = enclosing_scroll_frame_index();
|
||||
if (is_sticky_position() && sticky_frame_index.value()) {
|
||||
auto const& frame = scroll_state.frame_at(sticky_frame_index);
|
||||
if (frame.is_sticky() && frame.has_sticky_constraints()) {
|
||||
auto const& constraints = frame.sticky_constraints();
|
||||
auto const& insets = constraints.insets;
|
||||
recorder.compositor_sticky_area({
|
||||
.document_id = context.async_scrolling_document_id(),
|
||||
.scroll_frame_index = sticky_frame_index,
|
||||
.parent_scroll_frame_index = frame.parent_index(),
|
||||
.nearest_scrolling_ancestor_index = scroll_state.nearest_scrolling_ancestor(sticky_frame_index),
|
||||
.position_relative_to_scroll_ancestor = css_point_to_device_point(constraints.position_relative_to_scroll_ancestor, device_pixels_per_css_pixel),
|
||||
.border_box_size = css_size_to_device_size(constraints.border_box_size, device_pixels_per_css_pixel),
|
||||
.scrollport_size = css_size_to_device_size(constraints.scrollport_size, device_pixels_per_css_pixel),
|
||||
.containing_block_region = css_rect_to_device_rect(constraints.containing_block_region, device_pixels_per_css_pixel),
|
||||
.needs_parent_offset_adjustment = constraints.needs_parent_offset_adjustment,
|
||||
.inset_top = css_inset_to_device_inset(insets.top, device_pixels_per_css_pixel),
|
||||
.inset_right = css_inset_to_device_inset(insets.right, device_pixels_per_css_pixel),
|
||||
.inset_bottom = css_inset_to_device_inset(insets.bottom, device_pixels_per_css_pixel),
|
||||
.inset_left = css_inset_to_device_inset(insets.left, device_pixels_per_css_pixel),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PaintableBox::paint(DisplayListRecordingContext& context, PaintPhase phase) const
|
||||
{
|
||||
if (phase == PaintPhase::Background)
|
||||
Compositor::record_async_scrolling_metadata_for_paintable(*this, context);
|
||||
record_async_scrolling_metadata(context);
|
||||
|
||||
if (!is_visible())
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ public:
|
|||
virtual void reset_for_relayout();
|
||||
|
||||
virtual void paint(DisplayListRecordingContext&, PaintPhase) const override;
|
||||
void record_async_scrolling_metadata(DisplayListRecordingContext&) const;
|
||||
|
||||
RefPtr<StackingContext> stacking_context();
|
||||
RefPtr<StackingContext const> stacking_context() const;
|
||||
|
|
|
|||
|
|
@ -8,9 +8,12 @@
|
|||
#include <LibWeb/CSS/PropertyID.h>
|
||||
#include <LibWeb/CSS/VisualViewport.h>
|
||||
#include <LibWeb/DOM/Document.h>
|
||||
#include <LibWeb/DOM/EventTarget.h>
|
||||
#include <LibWeb/DOM/Range.h>
|
||||
#include <LibWeb/DOM/Text.h>
|
||||
#include <LibWeb/HTML/HTMLHtmlElement.h>
|
||||
#include <LibWeb/HTML/Navigable.h>
|
||||
#include <LibWeb/HTML/Window.h>
|
||||
#include <LibWeb/Layout/TextNode.h>
|
||||
#include <LibWeb/Layout/TextOffsetMapping.h>
|
||||
#include <LibWeb/Layout/Viewport.h>
|
||||
|
|
@ -19,6 +22,7 @@
|
|||
#include <LibWeb/Painting/Blending.h>
|
||||
#include <LibWeb/Painting/DevicePixelConverter.h>
|
||||
#include <LibWeb/Painting/DisplayListRecorder.h>
|
||||
#include <LibWeb/Painting/DisplayListRecordingContext.h>
|
||||
#include <LibWeb/Painting/ResolvedCSSFilter.h>
|
||||
#include <LibWeb/Painting/ScrollFrame.h>
|
||||
#include <LibWeb/Painting/StackingContext.h>
|
||||
|
|
@ -39,6 +43,53 @@ ViewportPaintable::ViewportPaintable(Layout::Viewport const& layout_viewport)
|
|||
|
||||
ViewportPaintable::~ViewportPaintable() = default;
|
||||
|
||||
struct BlockingWheelEventRegionState {
|
||||
bool has_blocking_wheel_event_listeners { false };
|
||||
bool has_blocking_wheel_event_region_covering_viewport { false };
|
||||
};
|
||||
|
||||
static BlockingWheelEventRegionState collect_root_blocking_wheel_event_regions(DOM::Document& document)
|
||||
{
|
||||
DOM::EventTarget* roots[] = {
|
||||
document.navigable() ? document.navigable()->active_window() : nullptr,
|
||||
&document,
|
||||
document.document_element(),
|
||||
document.body(),
|
||||
};
|
||||
for (auto* target : roots) {
|
||||
if (target && target->has_blocking_wheel_event_listener()) {
|
||||
return {
|
||||
.has_blocking_wheel_event_listeners = true,
|
||||
.has_blocking_wheel_event_region_covering_viewport = true,
|
||||
};
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
void ViewportPaintable::initialize_async_scrolling_metadata_recording(DisplayListRecordingContext& context)
|
||||
{
|
||||
auto blocking_wheel_event_region_state = collect_root_blocking_wheel_event_regions(document());
|
||||
context.set_async_scrolling_metadata_context(
|
||||
document().unique_id(),
|
||||
scroll_state(),
|
||||
blocking_wheel_event_region_state.has_blocking_wheel_event_listeners,
|
||||
blocking_wheel_event_region_state.has_blocking_wheel_event_region_covering_viewport);
|
||||
}
|
||||
|
||||
void ViewportPaintable::finalize_async_scrolling_metadata_recording(DisplayListRecordingContext& context, HTML::Navigable& navigable, Gfx::IntRect viewport_rect)
|
||||
{
|
||||
if (!context.is_recording_async_scrolling_metadata())
|
||||
return;
|
||||
|
||||
context.display_list_recorder().set_async_scrolling_metadata({
|
||||
.viewport_rect = viewport_rect,
|
||||
.wheel_event_listener_state_generation = navigable.page().wheel_event_listener_state_generation(),
|
||||
.has_blocking_wheel_event_listeners = context.has_blocking_wheel_event_listeners(),
|
||||
.has_blocking_wheel_event_region_covering_viewport = context.has_blocking_wheel_event_region_covering_viewport(),
|
||||
});
|
||||
}
|
||||
|
||||
void ViewportPaintable::reset_for_relayout()
|
||||
{
|
||||
PaintableWithLines::reset_for_relayout();
|
||||
|
|
|
|||
|
|
@ -22,6 +22,8 @@ public:
|
|||
virtual void reset_for_relayout() override;
|
||||
|
||||
void paint_all_phases(DisplayListRecordingContext&);
|
||||
void initialize_async_scrolling_metadata_recording(DisplayListRecordingContext&);
|
||||
void finalize_async_scrolling_metadata_recording(DisplayListRecordingContext&, HTML::Navigable&, Gfx::IntRect viewport_rect);
|
||||
void build_stacking_context_tree_if_needed();
|
||||
|
||||
void assign_scroll_frames();
|
||||
|
|
|
|||
Loading…
Reference in a new issue