diff --git a/Libraries/LibWeb/DOM/Document.cpp b/Libraries/LibWeb/DOM/Document.cpp index 451af006bc675..e3f5f5a17f406 100644 --- a/Libraries/LibWeb/DOM/Document.cpp +++ b/Libraries/LibWeb/DOM/Document.cpp @@ -1807,7 +1807,7 @@ void Document::recompute_containing_block_and_derive_abspos_escape_flags(Layout: // Refreshes every structure derived from committed layout results, shared by the partial and // full layout paths so neither can forget one. -void Document::after_layout_commit(LayoutTreeChanged layout_tree_changed) +void Document::after_layout_commit(LayoutTreeChanged layout_tree_changed, LayoutCommitScope layout_commit_scope, ReadonlySpan boxes_needing_eager_overflow_measurement) { // NB: Called during layout update. m_layout_root->invalidate_text_blocks_cache(); @@ -1821,7 +1821,10 @@ void Document::after_layout_commit(LayoutTreeChanged layout_tree_changed) // recalculation rebuilds the map inside its own measurement traversal instead. if (layout_tree_changed == LayoutTreeChanged::Yes && !m_needs_full_scrollable_overflow_recalculation) m_scrollable_overflow_contained_boxes_from_last_layout = Layout::collect_scrollable_overflow_contained_boxes(*m_layout_root); - update_scrollable_overflow(ScrollableOverflowDerivedStructureUpdates::HandledByAfterLayoutCommit); + if (layout_commit_scope == LayoutCommitScope::Full) + update_scrollable_overflow(ScrollableOverflowDerivedStructureUpdates::HandledByFullLayoutCommit, boxes_needing_eager_overflow_measurement); + else + update_scrollable_overflow(ScrollableOverflowDerivedStructureUpdates::HandledByAfterLayoutCommit); set_needs_accumulated_visual_contexts_update(true); @@ -2108,7 +2111,7 @@ Document::PartialRelayoutResult Document::try_partial_relayout(HashTable boxes_needing_eager_overflow_measurement; bridge.run_root_layout( *m_layout_root, viewport_rect.width(), viewport_rect.height(), should_collect_devtools_layout_data); + m_scrollable_overflow_contained_boxes_from_last_layout = Layout::collect_scrollable_overflow_contained_boxes( + *m_layout_root, [&](Layout::Box const& box) { + auto paintable = box.paintable_box(); + if (&box == m_layout_root.ptr() || box.is_scroll_container() || (paintable && !paintable->scroll_offset().is_zero())) + boxes_needing_eager_overflow_measurement.append(&box); + }); style_invalidation_counters().relayouts_performed++; @@ -2223,7 +2233,7 @@ void Document::update_layout(UpdateLayoutReason reason) ++m_full_layout_count; - after_layout_commit(LayoutTreeChanged::Yes); + after_layout_commit(LayoutTreeChanged::Yes, LayoutCommitScope::Full, boxes_needing_eager_overflow_measurement); m_layout_root->for_each_in_inclusive_subtree([](auto& node) { node.reset_needs_layout_update(); @@ -2481,7 +2491,7 @@ static void rebuild_sticky_insets(Layout::Node const& root) }); } -void Document::update_scrollable_overflow(ScrollableOverflowDerivedStructureUpdates derived_structure_updates) +void Document::update_scrollable_overflow(ScrollableOverflowDerivedStructureUpdates derived_structure_updates, ReadonlySpan boxes_needing_eager_measurement) { // For every box that will be re-measured, the overflow data it had before, so the diff below // can tell what actually changed; an empty value means the box's paintable was reset by a @@ -2497,6 +2507,30 @@ void Document::update_scrollable_overflow(ScrollableOverflowDerivedStructureUpda style_invalidation_counters().scrollable_overflow_recalculations++; + // The scroll offset can become invalid if the scrollable overflow rectangle has changed. For + // example, if the scroll container has been scrolled to the very end and then its scrollable + // overflow rect becomes smaller, the scroll offset would be out of bounds. Re-applying the + // current offset clamps it against the new rect. + auto clamp_scroll_offset = [](Painting::Paintable& paintable) { + if (!paintable.scroll_offset().is_zero()) + paintable.set_scroll_offset(paintable.scroll_offset()); + }; + + if (derived_structure_updates == ScrollableOverflowDerivedStructureUpdates::HandledByFullLayoutCommit) { + VERIFY(needs_full_recalculation); + + // A full-root commit reset every surviving paintable, including its overflow data and + // paint cache. There is therefore no old overflow to preserve or diff. Ordinary boxes are + // measured recursively when their overflow contributes to one of these roots, so they do + // not need separate eager measurement. + for (auto const* box : boxes_needing_eager_measurement) { + Layout::measure_scrollable_overflow(*box, m_scrollable_overflow_contained_boxes_from_last_layout); + if (auto box_paintable = box->paintable_box()) + clamp_scroll_offset(const_cast(*box_paintable)); + } + return; + } + auto record_and_clear_overflow_data = [&](Layout::Box const& box) { auto box_paintable = box.paintable_box(); if (!box_paintable) @@ -2528,6 +2562,8 @@ void Document::update_scrollable_overflow(ScrollableOverflowDerivedStructureUpda }); } for (auto const* containing_block = box->containing_block(); containing_block; containing_block = containing_block->containing_block()) { + if (auto containing_block_paintable = containing_block->paintable_box()) + const_cast(*containing_block_paintable).clear_cached_overflow_data(); if (!record_and_clear_overflow_data(*containing_block)) break; } @@ -2537,19 +2573,19 @@ void Document::update_scrollable_overflow(ScrollableOverflowDerivedStructureUpda if (old_overflow_data_by_box.is_empty()) return; - // The scroll offset can become invalid if the scrollable overflow rectangle has changed. For - // example, if the scroll container has been scrolled to the very end and then its scrollable - // overflow rect becomes smaller, the scroll offset would be out of bounds. Re-applying the - // current offset clamps it against the new rect. - auto clamp_scroll_offset = [](Painting::Paintable& paintable) { - if (!paintable.scroll_offset().is_zero()) - paintable.set_scroll_offset(paintable.scroll_offset()); - }; - for (auto const& it : old_overflow_data_by_box) { + auto box_paintable = it.key->paintable_box(); + if (!box_paintable) + continue; + + // Boxes reset by a subtree commit have no previous overflow data. They will be measured + // recursively if an ancestor reaches them. Measuring each one here would repeatedly walk + // the same containing-block chains after a small subtree update. + if (!it.value.has_value() && it.key != m_layout_root.ptr() && !it.key->is_scroll_container() && box_paintable->scroll_offset().is_zero()) + continue; + Layout::measure_scrollable_overflow(*it.key, m_scrollable_overflow_contained_boxes_from_last_layout); - if (auto box_paintable = it.key->paintable_box()) - clamp_scroll_offset(const_cast(*box_paintable)); + clamp_scroll_offset(const_cast(*box_paintable)); } bool any_overflow_changed = false; @@ -2599,6 +2635,14 @@ void Document::update_scrollable_overflow(ScrollableOverflowDerivedStructureUpda m_document->set_needs_repaint(); } +void Document::ensure_scrollable_overflow_is_measured(Layout::Box const& box) const +{ + auto paintable = box.paintable_box(); + if (!paintable || paintable->overflow_data().has_value() || paintable->cached_overflow_data().has_value()) + return; + Layout::measure_scrollable_overflow(box, m_scrollable_overflow_contained_boxes_from_last_layout); +} + void Document::update_paint_and_hit_testing_properties_if_needed() { // NB: Called during paint property resolution. @@ -8872,6 +8916,13 @@ void Document::schedule_scrollable_overflow_recalculation(Layout::Node const& la return; } + if (auto const* box = as_if(layout_node)) { + for (auto const* containing_box = box; containing_box; containing_box = containing_box->containing_block()) { + if (auto paintable = containing_box->paintable_box()) + const_cast(*paintable).clear_cached_overflow_data(); + } + } + if (m_needs_full_scrollable_overflow_recalculation) return; diff --git a/Libraries/LibWeb/DOM/Document.h b/Libraries/LibWeb/DOM/Document.h index c2bc00f5e4f83..6d7511bf619bf 100644 --- a/Libraries/LibWeb/DOM/Document.h +++ b/Libraries/LibWeb/DOM/Document.h @@ -475,8 +475,10 @@ class WEB_API Document enum class ScrollableOverflowDerivedStructureUpdates : u8 { UpdateAfterMeasure, HandledByAfterLayoutCommit, + HandledByFullLayoutCommit, }; - void update_scrollable_overflow(ScrollableOverflowDerivedStructureUpdates); + void ensure_scrollable_overflow_is_measured(Layout::Box const&) const; + void update_scrollable_overflow(ScrollableOverflowDerivedStructureUpdates, ReadonlySpan boxes_needing_eager_measurement = {}); void update_paint_and_hit_testing_properties_if_needed(); void update_animated_style_if_needed(); void update_style_computer_viewport_rect(); @@ -1425,7 +1427,11 @@ class WEB_API Document No, Yes, }; - void after_layout_commit(LayoutTreeChanged); + enum class LayoutCommitScope : u8 { + Subtree, + Full, + }; + void after_layout_commit(LayoutTreeChanged, LayoutCommitScope, ReadonlySpan boxes_needing_eager_overflow_measurement = {}); void run_unloading_cleanup_steps(); @@ -1799,8 +1805,7 @@ class WEB_API Document Vector> m_paintable_boxes_needing_scrollable_overflow_recalculation; // NB: Holds raw layout node pointers that are only safe to read while m_layout_root still owns // the tree they came from: every full layout rebuilds the map, layout tree teardown clears - // it, and its only reader, the scheduled scrollable overflow recalculation, runs only when - // layout is up to date. + // it, and overflow measurement only reads it while that root remains current. Layout::ContainedBoxesMap m_scrollable_overflow_contained_boxes_from_last_layout; CSS::SheetSetStyleCacheRegistry m_sheet_set_style_cache_registry; RefPtr m_hit_test_display_list; diff --git a/Libraries/LibWeb/Layout/LayoutRustBridge.cpp b/Libraries/LibWeb/Layout/LayoutRustBridge.cpp index 91a072d7044dd..757b45cce300a 100644 --- a/Libraries/LibWeb/Layout/LayoutRustBridge.cpp +++ b/Libraries/LibWeb/Layout/LayoutRustBridge.cpp @@ -805,6 +805,7 @@ RustFFI::FfiCommitSink LayoutRustBridge::commit_sink() }, .set_box_metrics = [](void*, void* paintable_pointer, RustFFI::FfiCommittedBoxMetrics metrics) { auto& paintable = *static_cast(paintable_pointer); + paintable.set_layout_fragment_identity(metrics.fragment_identity); auto& box_model = paintable.box_model(); box_model.inset = { CSSPixels::from_raw(metrics.inset_top), diff --git a/Libraries/LibWeb/Layout/Node.cpp b/Libraries/LibWeb/Layout/Node.cpp index 00a29ea7f63e1..072769533f5df 100644 --- a/Libraries/LibWeb/Layout/Node.cpp +++ b/Libraries/LibWeb/Layout/Node.cpp @@ -138,10 +138,12 @@ void Node::bump_fragment_cache_epoch() // into a fail-safe miss. void Node::bump_fragment_cache_epoch_of_self_and_ancestors() { - if (!fragment_cache_epochs_enabled()) - return; - for (auto* node = this; node; node = node->parent_ptr()) - ++node->node_data().fragment_cache_epoch; + for (auto* node = this; node; node = node->parent_ptr()) { + if (fragment_cache_epochs_enabled()) + ++node->node_data().fragment_cache_epoch; + if (auto* box = as_if(*node); box && box->paintable_box()) + const_cast(*box->paintable_box()).clear_cached_overflow_data(); + } } void* Node::arena_handle() const diff --git a/Libraries/LibWeb/Layout/ScrollableOverflow.cpp b/Libraries/LibWeb/Layout/ScrollableOverflow.cpp index 029e81a9b0a9a..ad78fca87af95 100644 --- a/Libraries/LibWeb/Layout/ScrollableOverflow.cpp +++ b/Libraries/LibWeb/Layout/ScrollableOverflow.cpp @@ -89,9 +89,11 @@ PhysicalOverflowDirections physical_overflow_directions(Box const& box) }; } -static CSSPixelRect apply_css_transform_to_overflow_rect(Box const& box, CSSPixelRect const& rect) +static CSSPixelRect apply_css_transform_to_overflow_rect(Box const& box, CSSPixelRect const& rect, bool has_css_transform) { auto const& paintable_box = *box.paintable_box(); + if (!has_css_transform) + return rect; auto transform_data = Painting::compute_transform(paintable_box, 1.0); if (!transform_data.has_value()) return rect; @@ -143,21 +145,43 @@ CSSPixelRect measure_scrollable_overflow(Box const& box, ContainedBoxesMap const auto const& paintable_box = *box.paintable_box(); - if (paintable_box.scrollable_overflow_rect().has_value()) - return paintable_box.scrollable_overflow_rect().value(); + if (paintable_box.overflow_data().has_value()) + return paintable_box.overflow_data()->scrollable_overflow_rect; + + auto const paintable_absolute_padding_box = paintable_box.absolute_padding_box_rect(); + auto const paintable_absolute_content_box = paintable_box.absolute_rect(); + + auto store_overflow_data = [&](Painting::Paintable::OverflowData overflow_data) { + const_cast(paintable_box).set_overflow_data(overflow_data); + + auto rect_relative_to_padding_box = overflow_data.scrollable_overflow_rect; + rect_relative_to_padding_box.translate_by({ -paintable_absolute_padding_box.x(), -paintable_absolute_padding_box.y() }); + const_cast(paintable_box).set_cached_overflow_data({ + .rect_relative_to_padding_box = rect_relative_to_padding_box, + .has_scrollable_overflow = overflow_data.has_scrollable_overflow, + }); + }; + + if (auto const& cached_overflow = paintable_box.cached_overflow_data(); cached_overflow.has_value()) { + auto scrollable_overflow_rect = cached_overflow->rect_relative_to_padding_box; + scrollable_overflow_rect.translate_by(paintable_absolute_padding_box.location()); + const_cast(paintable_box).set_overflow_data({ + .scrollable_overflow_rect = scrollable_overflow_rect, + .has_scrollable_overflow = cached_overflow->has_scrollable_overflow, + }); + return scrollable_overflow_rect; + } // https://drafts.csswg.org/css-overflow-3/#scrollable-overflow-calculation // The scrollable overflow area of a box is the union of: // - Its own padding box. - auto const paintable_absolute_padding_box = paintable_box.absolute_padding_box_rect(); - auto const paintable_absolute_content_box = paintable_box.absolute_rect(); auto scrollable_overflow_rect = paintable_absolute_padding_box; auto in_flow_and_floated_content_bounds = paintable_absolute_content_box; // Replaced SVG viewports clip their content if (is(box)) { - const_cast(paintable_box).set_overflow_data({ + store_overflow_data({ .scrollable_overflow_rect = scrollable_overflow_rect, .has_scrollable_overflow = false, }); @@ -206,8 +230,31 @@ CSSPixelRect measure_scrollable_overflow(Box const& box, ContainedBoxesMap const if (child.is_fixed_position()) continue; - auto untransformed_child_border_box = child.paintable_box()->absolute_border_box_rect(); - auto child_border_box = apply_css_transform_to_overflow_rect(child, untransformed_child_border_box); + auto const& child_paintable = *child.paintable_box(); + auto const child_has_css_transform = child_paintable.has_css_transform(); + if (child.position() == CSS::Positioning::Static && child.display().is_inline_outside() && !child.is_floating() && !child_has_css_transform) { + if (auto const& cached_overflow = child_paintable.cached_overflow_data(); cached_overflow.has_value()) { + auto const& border = child_paintable.box_model().border; + auto const has_border = border.top != 0 || border.right != 0 || border.bottom != 0 || border.left != 0; + if (!has_border) { + auto const& padding = child_paintable.box_model().padding; + CSSPixelRect content_box_relative_to_padding_box { + { padding.left, padding.top }, + { + child_paintable.content_width(), + child_paintable.content_height(), + }, + }; + // The committed line fragment already contributes this content box. A box with no border whose + // cached overflow fits inside the content box cannot expand its containing block's overflow. + if (content_box_relative_to_padding_box.contains(cached_overflow->rect_relative_to_padding_box)) + continue; + } + } + } + + auto untransformed_child_border_box = child_paintable.absolute_border_box_rect(); + auto child_border_box = apply_css_transform_to_overflow_rect(child, untransformed_child_border_box, child_has_css_transform); // NOTE: Only boxes that are not wholly in the unreachable scrollable overflow region contribute. auto wholly_in_unreachable_horizontal_axis = overflow_directions.horizontal_axis_is_positive @@ -234,13 +281,15 @@ CSSPixelRect measure_scrollable_overflow(Box const& box, ContainedBoxesMap const if (child.has_layout_containment() || child.has_paint_containment()) continue; - if (child.overflow_x() == CSS::Overflow::Visible || child.overflow_y() == CSS::Overflow::Visible) { - auto child_scrollable_overflow = apply_css_transform_to_overflow_rect(child, measure_scrollable_overflow(child, contained_boxes_map)); + auto const child_overflow_x = child.overflow_x(); + auto const child_overflow_y = child.overflow_y(); + if (child_overflow_x == CSS::Overflow::Visible || child_overflow_y == CSS::Overflow::Visible) { + auto child_scrollable_overflow = apply_css_transform_to_overflow_rect(child, measure_scrollable_overflow(child, contained_boxes_map), child_has_css_transform); if (!child_scrollable_overflow.is_empty()) { - if (child.overflow_x() == CSS::Overflow::Visible) { + if (child_overflow_x == CSS::Overflow::Visible) { scrollable_overflow_rect.unite_horizontally(child_scrollable_overflow); } - if (child.overflow_y() == CSS::Overflow::Visible) { + if (child_overflow_y == CSS::Overflow::Visible) { scrollable_overflow_rect.unite_vertically(child_scrollable_overflow); } } @@ -287,7 +336,7 @@ CSSPixelRect measure_scrollable_overflow(Box const& box, ContainedBoxesMap const has_scrollable_overflow = !paintable_absolute_padding_box.contains(scrollable_overflow_rect) && box.is_scroll_container(); } - const_cast(paintable_box).set_overflow_data({ + store_overflow_data({ .scrollable_overflow_rect = scrollable_overflow_rect, .has_scrollable_overflow = has_scrollable_overflow, }); diff --git a/Libraries/LibWeb/Painting/Paintable.cpp b/Libraries/LibWeb/Painting/Paintable.cpp index 48a9451a440f3..bf5f96d832877 100644 --- a/Libraries/LibWeb/Painting/Paintable.cpp +++ b/Libraries/LibWeb/Painting/Paintable.cpp @@ -1175,6 +1175,28 @@ CSSPixelPoint Paintable::minimum_scroll_offset() const }; } +bool Paintable::has_scrollable_overflow() const +{ + if (auto const* box = as_if(layout_node())) + document().ensure_scrollable_overflow_is_measured(*box); + if (m_overflow_data.has_value()) + return m_overflow_data->has_scrollable_overflow; + return m_cached_overflow_data.has_value() && m_cached_overflow_data->has_scrollable_overflow; +} + +Optional Paintable::scrollable_overflow_rect() const +{ + if (auto const* box = as_if(layout_node())) + document().ensure_scrollable_overflow_is_measured(*box); + if (m_overflow_data.has_value()) + return m_overflow_data->scrollable_overflow_rect; + if (!m_cached_overflow_data.has_value()) + return {}; + auto scrollable_overflow_rect = m_cached_overflow_data->rect_relative_to_padding_box; + scrollable_overflow_rect.translate_by(absolute_padding_box_rect().location()); + return scrollable_overflow_rect; +} + CSSPixelPoint Paintable::maximum_scroll_offset() const { auto scrollable_overflow_rect = this->scrollable_overflow_rect(); diff --git a/Libraries/LibWeb/Painting/Paintable.h b/Libraries/LibWeb/Painting/Paintable.h index b580ac94b8de3..93a16a4a0277e 100644 --- a/Libraries/LibWeb/Painting/Paintable.h +++ b/Libraries/LibWeb/Painting/Paintable.h @@ -141,6 +141,7 @@ class WEB_API Paintable [[nodiscard]] virtual bool is_svg_svg_paintable() const { return false; } [[nodiscard]] virtual bool is_svg_path_paintable() const { return false; } [[nodiscard]] virtual bool is_svg_graphics_paintable() const { return false; } + [[nodiscard]] virtual bool is_svg_foreign_object_paintable() const { return false; } DOM::Document const& document() const; DOM::Document& document(); @@ -223,6 +224,11 @@ class WEB_API Paintable bool has_scrollable_overflow { false }; }; + struct CachedOverflowData { + CSSPixelRect rect_relative_to_padding_box; + bool has_scrollable_overflow { false }; + }; + // Offset from the top left of the containing block's content edge. [[nodiscard]] CSSPixelPoint offset() const; @@ -281,12 +287,7 @@ class WEB_API Paintable CSSPixelPoint transform_to_local_coordinates(CSSPixelPoint position) const; - [[nodiscard]] bool has_scrollable_overflow() const - { - if (!m_overflow_data.has_value()) - return false; - return m_overflow_data->has_scrollable_overflow; - } + [[nodiscard]] bool has_scrollable_overflow() const; [[nodiscard]] bool has_css_transform() const; @@ -295,17 +296,24 @@ class WEB_API Paintable [[nodiscard]] bool overflow_property_applies() const; - [[nodiscard]] Optional scrollable_overflow_rect() const - { - if (!m_overflow_data.has_value()) - return {}; - return m_overflow_data->scrollable_overflow_rect; - } + [[nodiscard]] Optional scrollable_overflow_rect() const; [[nodiscard]] Optional const& overflow_data() const { return m_overflow_data; } void set_overflow_data(OverflowData data) { m_overflow_data = move(data); } void clear_overflow_data() { m_overflow_data.clear(); } + Optional const& cached_overflow_data() const { return m_cached_overflow_data; } + void set_cached_overflow_data(CachedOverflowData data) { m_cached_overflow_data = move(data); } + void clear_cached_overflow_data() { m_cached_overflow_data.clear(); } + void set_layout_fragment_identity(u64 identity) + { + VERIFY(identity); + if (m_layout_fragment_identity == identity) + return; + m_layout_fragment_identity = identity; + clear_cached_overflow_data(); + } + virtual void set_needs_repaint(InvalidateDisplayList = InvalidateDisplayList::Yes); virtual bool handle_mousewheel(Badge, CSSPixelPoint, unsigned buttons, unsigned modifiers, double wheel_delta_x, double wheel_delta_y); @@ -541,6 +549,8 @@ class WEB_API Paintable RefPtr m_stacking_context; Optional m_overflow_data; + Optional m_cached_overflow_data; + u64 m_layout_fragment_identity { 0 }; CSSPixelPoint m_offset; CSSPixelSize m_content_size; diff --git a/Libraries/LibWeb/Painting/SVGForeignObjectPaintable.h b/Libraries/LibWeb/Painting/SVGForeignObjectPaintable.h index 011d798f6a702..8a66b61712a20 100644 --- a/Libraries/LibWeb/Painting/SVGForeignObjectPaintable.h +++ b/Libraries/LibWeb/Painting/SVGForeignObjectPaintable.h @@ -31,6 +31,12 @@ class SVGForeignObjectPaintable final : public PaintableWithLines protected: SVGForeignObjectPaintable(Layout::SVGForeignObjectBox const&); + +private: + virtual bool is_svg_foreign_object_paintable() const override { return true; } }; +template<> +inline bool Paintable::fast_is() const { return is_svg_foreign_object_paintable(); } + } diff --git a/Libraries/LibWeb/Rust/src/layout/commit.rs b/Libraries/LibWeb/Rust/src/layout/commit.rs index 171fb96a6e805..83b16c52eb484 100644 --- a/Libraries/LibWeb/Rust/src/layout/commit.rs +++ b/Libraries/LibWeb/Rust/src/layout/commit.rs @@ -16,6 +16,7 @@ pub struct FfiTableCellCoordinates { #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] #[repr(C)] pub struct FfiCommittedBoxMetrics { + pub fragment_identity: u64, pub content_offset: crate::layout::FfiCssPixelPoint, pub content_inline_size: crate::layout::CssPixels, pub content_block_size: crate::layout::CssPixels, @@ -169,6 +170,7 @@ fn commit_subtree( sink.context, paintable, FfiCommittedBoxMetrics { + fragment_identity: fragment.identity, content_offset: link.committed_offset, content_inline_size: fragment.content_inline_size, content_block_size: fragment.content_block_size, diff --git a/Libraries/LibWeb/Rust/src/layout/fragment_tree.rs b/Libraries/LibWeb/Rust/src/layout/fragment_tree.rs index d193d2de101cf..fda04718ce98d 100644 --- a/Libraries/LibWeb/Rust/src/layout/fragment_tree.rs +++ b/Libraries/LibWeb/Rust/src/layout/fragment_tree.rs @@ -5,6 +5,7 @@ */ pub(crate) struct Fragment { + pub(crate) identity: u64, pub(crate) node: crate::layout::node_data::NodeSlotId, pub(crate) content_inline_size: CssPixels, pub(crate) content_block_size: CssPixels, @@ -156,6 +157,7 @@ fn snapshot_fragment( children: Vec, used: &UsedValues, ) -> std::rc::Rc { + static NEXT_IDENTITY: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(1); let line_data = used.line_data.get().map(std::cell::RefCell::take); let rare_payloads = used.rare_data.get().map(|cell| { let mut rare = cell.borrow_mut(); @@ -181,6 +183,7 @@ fn snapshot_fragment( computed_svg_path, ) = rare_payloads.unwrap_or_default(); std::rc::Rc::new(Fragment { + identity: NEXT_IDENTITY.fetch_add(1, std::sync::atomic::Ordering::Relaxed), node, content_inline_size: used.content_inline_size.get(), content_block_size: used.content_block_size.get(), diff --git a/Tests/LibWeb/Text/expected/css/scrollable-overflow-cache-validity.txt b/Tests/LibWeb/Text/expected/css/scrollable-overflow-cache-validity.txt new file mode 100644 index 0000000000000..4bee1c5e983b1 --- /dev/null +++ b/Tests/LibWeb/Text/expected/css/scrollable-overflow-cache-validity.txt @@ -0,0 +1,22 @@ +percentage size: PASS +nested percentage size: PASS +min max constrained size: PASS +absolute percentage geometry: PASS +relative percentage offset: PASS +percentage transform: PASS +wrapped text: PASS +inline box wrapping: PASS +flex wrapping: PASS +flex percentage basis: PASS +grid percentage tracks: PASS +grid item placement: PASS +nested scroll containers: PASS +clipping boundary: PASS +layout containment boundary: PASS +vertical writing mode: PASS +inherited font size: PASS +aspect ratio: PASS +descendant insertion: PASS +descendant removal: PASS +display tree rebuild: PASS +containing block change: PASS diff --git a/Tests/LibWeb/Text/expected/css/scrollable-overflow-cache-viewport-resize.txt b/Tests/LibWeb/Text/expected/css/scrollable-overflow-cache-viewport-resize.txt new file mode 100644 index 0000000000000..00b9742a3de5f --- /dev/null +++ b/Tests/LibWeb/Text/expected/css/scrollable-overflow-cache-viewport-resize.txt @@ -0,0 +1,2 @@ +before viewport resize: scroller=200x150 child=200x150 +after viewport resize: scroller=100x100 child=100x80 diff --git a/Tests/LibWeb/Text/expected/css/scrollable-overflow-measurement-directions.txt b/Tests/LibWeb/Text/expected/css/scrollable-overflow-measurement-directions.txt new file mode 100644 index 0000000000000..b3d119fb2f91c --- /dev/null +++ b/Tests/LibWeb/Text/expected/css/scrollable-overflow-measurement-directions.txt @@ -0,0 +1,18 @@ +ltr-negative-x: 100x100 +ltr-positive-x: 180x100 +rtl-negative-x: 180x100 +rtl-positive-x: 100x100 +horizontal-negative-y: 100x100 +horizontal-positive-y: 100x180 +vertical-rl-negative-x: 180x100 +vertical-rl-positive-x: 100x100 +vertical-lr-negative-x: 100x100 +vertical-lr-positive-x: 180x100 +vertical-rtl-negative-y: 100x180 +vertical-rtl-positive-y: 100x100 +sideways-rl-negative-x: 180x100 +sideways-rl-positive-x: 100x100 +sideways-lr-negative-x: 100x100 +sideways-lr-positive-x: 180x100 +sideways-lr-negative-y: 100x180 +sideways-lr-positive-y: 100x100 diff --git a/Tests/LibWeb/Text/expected/css/scrollable-overflow-measurement-dynamic.txt b/Tests/LibWeb/Text/expected/css/scrollable-overflow-measurement-dynamic.txt new file mode 100644 index 0000000000000..8419a17ddf3a6 --- /dev/null +++ b/Tests/LibWeb/Text/expected/css/scrollable-overflow-measurement-dynamic.txt @@ -0,0 +1,12 @@ +initial: 100x100 +after transform: 210x240 +after clipping: 100x100 +after unclipping: 210x240 +after relayout: 230x260 +at maximum: 130,160 +after shrink: 100x100 +after clamp: 0,0 +after insertion: 300x320 +after removal: 100x100 +before moving reused subtree: 100x130 +after moving reused subtree: 100x230 diff --git a/Tests/LibWeb/Text/expected/css/scrollable-overflow-measurement.txt b/Tests/LibWeb/Text/expected/css/scrollable-overflow-measurement.txt new file mode 100644 index 0000000000000..bc4466d1328e1 --- /dev/null +++ b/Tests/LibWeb/Text/expected/css/scrollable-overflow-measurement.txt @@ -0,0 +1,27 @@ +direct: 150x160 +nested-visible: 150x160 +nested-clipped: 100x100 +transformed: 160x180 +positioned: 160x180 +fixed: 100x100 +paint-contained: 100x100 +layout-contained: 100x100 +zero-area: 100x100 +visible-overflow: 170x180 +line-box: 190x100 +line-box-border-padding: 120x120 +relative-positioned: 160x180 +relative-positioned-inline: 160x180 +floated: 150x160 +visible-x-clipped-y: 150x100 +clipped-x-visible-y: 100x160 +padded-in-flow: 170x180 +nested-scroll-container: 100x100 +inner-scroll-container: 200x210 +replaced-svg: 100x100 +clipped-barrier: 200x210 +inside-clipped-barrier: 200x210 +contained-barrier: 220x230 +inside-contained-barrier: 220x230 +before containing block resize: 300x150 +after containing block resize: 100x50 diff --git a/Tests/LibWeb/Text/input/css/scrollable-overflow-cache-validity.html b/Tests/LibWeb/Text/input/css/scrollable-overflow-cache-validity.html new file mode 100644 index 0000000000000..875be1e98c71c --- /dev/null +++ b/Tests/LibWeb/Text/input/css/scrollable-overflow-cache-validity.html @@ -0,0 +1,208 @@ + + + + + diff --git a/Tests/LibWeb/Text/input/css/scrollable-overflow-cache-viewport-resize.html b/Tests/LibWeb/Text/input/css/scrollable-overflow-cache-viewport-resize.html new file mode 100644 index 0000000000000..c73b4d0e368fa --- /dev/null +++ b/Tests/LibWeb/Text/input/css/scrollable-overflow-cache-viewport-resize.html @@ -0,0 +1,27 @@ + + + + diff --git a/Tests/LibWeb/Text/input/css/scrollable-overflow-measurement-directions.html b/Tests/LibWeb/Text/input/css/scrollable-overflow-measurement-directions.html new file mode 100644 index 0000000000000..89dfc4b328af8 --- /dev/null +++ b/Tests/LibWeb/Text/input/css/scrollable-overflow-measurement-directions.html @@ -0,0 +1,54 @@ + + + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + diff --git a/Tests/LibWeb/Text/input/css/scrollable-overflow-measurement-dynamic.html b/Tests/LibWeb/Text/input/css/scrollable-overflow-measurement-dynamic.html new file mode 100644 index 0000000000000..4dec4369c74c3 --- /dev/null +++ b/Tests/LibWeb/Text/input/css/scrollable-overflow-measurement-dynamic.html @@ -0,0 +1,58 @@ + + + + +
+
+
+
+
+
+ + diff --git a/Tests/LibWeb/Text/input/css/scrollable-overflow-measurement.html b/Tests/LibWeb/Text/input/css/scrollable-overflow-measurement.html new file mode 100644 index 0000000000000..23f5170aa24e9 --- /dev/null +++ b/Tests/LibWeb/Text/input/css/scrollable-overflow-measurement.html @@ -0,0 +1,169 @@ + + + + +
+
+
+ +
+
+
+
+
+ +
+
+
+
+
+ +
+
+
+ +
+
+
+ +
+
+
+ +
+
+
+
+
+ +
+
+
+
+
+ +
+
+
+ +
+
+
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ + + +
+ +
+
+
+ +
+
+
+
+
+ +
+
+
+
+
+ +
+
+
+ +
+
+
+
+
+ +
+ + + +
+ +
+
+
+
+
+ +
+
+
+
+
+ +
+
+
+ +