From a0be82b2cb07c30740935ee0c375ce2386d37114 Mon Sep 17 00:00:00 2001 From: Psychpsyo Date: Fri, 9 May 2025 21:34:47 +0200 Subject: [PATCH] LibWeb: Move containment checks to Layout::Node It fits better there and avoids having to reach into the Element all the time. --- Libraries/LibWeb/DOM/Document.cpp | 3 - Libraries/LibWeb/DOM/Element.cpp | 125 ---------------- Libraries/LibWeb/DOM/Element.h | 7 - Libraries/LibWeb/Layout/Box.cpp | 6 +- Libraries/LibWeb/Layout/Box.h | 5 - Libraries/LibWeb/Layout/FormattingContext.cpp | 7 +- Libraries/LibWeb/Layout/Node.cpp | 135 ++++++++++++++++-- Libraries/LibWeb/Layout/Node.h | 7 + Libraries/LibWeb/Layout/TreeBuilder.cpp | 2 +- .../LibWeb/Painting/ViewportPaintable.cpp | 17 +-- 10 files changed, 145 insertions(+), 169 deletions(-) diff --git a/Libraries/LibWeb/DOM/Document.cpp b/Libraries/LibWeb/DOM/Document.cpp index 21749f196be..7d719a8baa1 100644 --- a/Libraries/LibWeb/DOM/Document.cpp +++ b/Libraries/LibWeb/DOM/Document.cpp @@ -1333,9 +1333,6 @@ void Document::update_layout(UpdateLayoutReason reason) }); m_layout_root->for_each_in_inclusive_subtree_of_type([&](auto& child) { - if (auto dom_node = child.dom_node(); dom_node && dom_node->is_element()) { - child.set_has_size_containment(as(*dom_node).has_size_containment()); - } if (child.needs_layout_update()) { child.reset_cached_intrinsic_sizes(); } diff --git a/Libraries/LibWeb/DOM/Element.cpp b/Libraries/LibWeb/DOM/Element.cpp index a280eb32ae4..58125de6307 100644 --- a/Libraries/LibWeb/DOM/Element.cpp +++ b/Libraries/LibWeb/DOM/Element.cpp @@ -3188,131 +3188,6 @@ bool Element::skips_its_contents() return false; } -// https://drafts.csswg.org/css-contain-2/#containment-size -bool Element::has_size_containment() const -{ - // However, giving an element size containment has no effect if any of the following are true: - - // - if the element does not generate a principal box (as is the case with 'display: contents' or 'display: none') - if (!layout_node()) - return false; - - // - if its inner display type is 'table' - if (layout_node()->display().is_table_inside()) - return false; - - // - if its principal box is an internal table box - if (layout_node()->display().is_internal_table()) - return false; - - // - if its principal box is an internal ruby box or a non-atomic inline-level box - // FIXME: Implement this. - - if (computed_properties()->contain().size_containment) - return true; - - return false; -} -// https://drafts.csswg.org/css-contain-2/#containment-inline-size -bool Element::has_inline_size_containment() const -{ - // Giving an element inline-size containment has no effect if any of the following are true: - - // - if the element does not generate a principal box (as is the case with 'display: contents' or 'display: none') - if (!layout_node()) - return false; - - // - if its inner display type is 'table' - if (layout_node()->display().is_table_inside()) - return false; - - // - if its principal box is an internal table box - if (layout_node()->display().is_internal_table()) - return false; - - // - if its principal box is an internal ruby box or a non-atomic inline-level box - // FIXME: Implement this. - - if (computed_properties()->contain().inline_size_containment) - return true; - - return false; -} -// https://drafts.csswg.org/css-contain-2/#containment-layout -bool Element::has_layout_containment() const -{ - // However, giving an element layout containment has no effect if any of the following are true: - - // - if the element does not generate a principal box (as is the case with 'display: contents' or 'display: none') - if (!layout_node()) - return false; - - // - if its principal box is an internal table box other than 'table-cell' - if (layout_node()->display().is_internal_table() && !layout_node()->display().is_table_cell()) - return false; - - // - if its principal box is an internal ruby box or a non-atomic inline-level box - // FIXME: Implement this. - - if (computed_properties()->contain().layout_containment) - return true; - - // https://drafts.csswg.org/css-contain-2/#valdef-content-visibility-auto - // Changes the used value of the 'contain' property so as to turn on layout containment, style containment, and - // paint containment for the element. - if (computed_properties()->content_visibility() == CSS::ContentVisibility::Auto) - return true; - - return false; -} -// https://drafts.csswg.org/css-contain-2/#containment-style -bool Element::has_style_containment() const -{ - // However, giving an element style containment has no effect if any of the following are true: - - // - if the element does not generate a principal box (as is the case with 'display: contents' or 'display: none') - if (!layout_node()) - return false; - - if (computed_properties()->contain().style_containment) - return true; - - // https://drafts.csswg.org/css-contain-2/#valdef-content-visibility-auto - // Changes the used value of the 'contain' property so as to turn on layout containment, style containment, and - // paint containment for the element. - if (computed_properties()->content_visibility() == CSS::ContentVisibility::Auto) - return true; - - return false; -} -// https://drafts.csswg.org/css-contain-2/#containment-paint -bool Element::has_paint_containment() const -{ - // However, giving an element paint containment has no effect if any of the following are true: - - // - if the element does not generate a principal box (as is the case with 'display: contents' or 'display: none') - if (!layout_node()) - return false; - - // - if its principal box is an internal table box other than 'table-cell' - if (layout_node()->display().is_internal_table() && !layout_node()->display().is_table_cell()) - return false; - - // - if its principal box is an internal ruby box or a non-atomic inline-level box - // FIXME: Implement this - - if (computed_properties()->contain().paint_containment) - return true; - - // https://drafts.csswg.org/css-contain-2/#valdef-content-visibility-auto - // Changes the used value of the 'contain' property so as to turn on layout containment, style containment, and - // paint containment for the element. - if (computed_properties()->content_visibility() == CSS::ContentVisibility::Auto) - return true; - - return false; -} - size_t Element::number_of_owned_list_items() const { auto number_of_owned_li_elements = 0; diff --git a/Libraries/LibWeb/DOM/Element.h b/Libraries/LibWeb/DOM/Element.h index 00b083f2d53..53e31f0b5e7 100644 --- a/Libraries/LibWeb/DOM/Element.h +++ b/Libraries/LibWeb/DOM/Element.h @@ -422,13 +422,6 @@ public: // https://drafts.csswg.org/css-contain-2/#skips-its-contents bool skips_its_contents(); - // https://drafts.csswg.org/css-contain-2/#containment-types - bool has_size_containment() const; - bool has_inline_size_containment() const; - bool has_layout_containment() const; - bool has_style_containment() const; - bool has_paint_containment() const; - bool matches_enabled_pseudo_class() const; bool matches_disabled_pseudo_class() const; bool matches_checked_pseudo_class() const; diff --git a/Libraries/LibWeb/Layout/Box.cpp b/Libraries/LibWeb/Layout/Box.cpp index 35266546697..b0b25255d4e 100644 --- a/Libraries/LibWeb/Layout/Box.cpp +++ b/Libraries/LibWeb/Layout/Box.cpp @@ -34,7 +34,7 @@ Optional Box::natural_width() const // https://drafts.csswg.org/css-contain-2/#containment-size // Replaced elements must be treated as having a natural width and height of 0 and no natural aspect // ratio. - if (m_has_size_containment) + if (has_size_containment()) return 0; return m_natural_width; } @@ -43,7 +43,7 @@ Optional Box::natural_height() const // https://drafts.csswg.org/css-contain-2/#containment-size // Replaced elements must be treated as having a natural width and height of 0 and no natural aspect // ratio. - if (m_has_size_containment) + if (has_size_containment()) return 0; return m_natural_height; } @@ -52,7 +52,7 @@ Optional Box::natural_aspect_ratio() const // https://drafts.csswg.org/css-contain-2/#containment-size // Replaced elements must be treated as having a natural width and height of 0 and no natural aspect // ratio. - if (m_has_size_containment) + if (has_size_containment()) return {}; return m_natural_aspect_ratio; } diff --git a/Libraries/LibWeb/Layout/Box.h b/Libraries/LibWeb/Layout/Box.h index d371b084699..854e14952f2 100644 --- a/Libraries/LibWeb/Layout/Box.h +++ b/Libraries/LibWeb/Layout/Box.h @@ -40,9 +40,6 @@ public: bool has_natural_height() const { return natural_height().has_value(); } bool has_natural_aspect_ratio() const { return natural_aspect_ratio().has_value(); } - bool has_size_containment() const { return m_has_size_containment; } - void set_has_size_containment(bool value) { m_has_size_containment = value; } - void set_natural_width(Optional width) { m_natural_width = width; } void set_natural_height(Optional height) { m_natural_height = height; } void set_natural_aspect_ratio(Optional ratio) { m_natural_aspect_ratio = ratio; } @@ -82,8 +79,6 @@ private: Optional m_natural_height; Optional m_natural_aspect_ratio; - bool m_has_size_containment { false }; - Vector> m_contained_abspos_children; OwnPtr mutable m_cached_intrinsic_sizes; diff --git a/Libraries/LibWeb/Layout/FormattingContext.cpp b/Libraries/LibWeb/Layout/FormattingContext.cpp index b3dfad619ba..fcfe36ca440 100644 --- a/Libraries/LibWeb/Layout/FormattingContext.cpp +++ b/Libraries/LibWeb/Layout/FormattingContext.cpp @@ -97,11 +97,8 @@ bool FormattingContext::creates_block_formatting_context(Box const& box) // https://drafts.csswg.org/css-contain-2/#containment-types // 1. The layout containment box establishes an independent formatting context. // 4. The paint containment box establishes an independent formatting context. - if (box.dom_node() && box.dom_node()->is_element()) { - auto element = as(box.dom_node()); - if (element->has_layout_containment() || element->has_paint_containment()) - return true; - } + if (box.has_layout_containment() || box.has_paint_containment()) + return true; if (box.parent()) { auto parent_display = box.parent()->display(); diff --git a/Libraries/LibWeb/Layout/Node.cpp b/Libraries/LibWeb/Layout/Node.cpp index f6853598cb1..c886dac5b92 100644 --- a/Libraries/LibWeb/Layout/Node.cpp +++ b/Libraries/LibWeb/Layout/Node.cpp @@ -98,11 +98,8 @@ bool Node::can_contain_boxes_with_position_absolute() const // containing block. // 4. The paint containment box establishes an absolute positioning containing block and a fixed positioning // containing block. - if (dom_node() && dom_node()->is_element()) { - auto element = as(dom_node()); - if (element->has_layout_containment() || element->has_paint_containment()) - return true; - } + if (has_layout_containment() || has_paint_containment()) + return true; return false; } @@ -244,11 +241,8 @@ bool Node::establishes_stacking_context() const // https://drafts.csswg.org/css-contain-2/#containment-types // 5. The layout containment box creates a stacking context. // 3. The paint containment box creates a stacking context. - if (dom_node() && dom_node()->is_element()) { - auto element = as(dom_node()); - if (element->has_layout_containment() || element->has_paint_containment()) - return true; - } + if (has_layout_containment() || has_paint_containment()) + return true; // https://drafts.fxtf.org/compositing/#mix-blend-mode // Applying a blendmode other than normal to the element must establish a new stacking context. @@ -959,6 +953,7 @@ void NodeWithStyle::apply_style(CSS::ComputedProperties const& computed_style) computed_values.set_isolation(computed_style.isolation()); computed_values.set_mix_blend_mode(computed_style.mix_blend_mode()); computed_values.set_view_transition_name(computed_style.view_transition_name()); + computed_values.set_contain(computed_style.contain()); computed_values.set_caret_color(computed_style.caret_color(*this)); @@ -1240,6 +1235,126 @@ CSS::UserSelect Node::user_select_used_value() const return computed_value; } +// https://drafts.csswg.org/css-contain-2/#containment-size +bool Node::has_size_containment() const +{ + // However, giving an element size containment has no effect if any of the following are true: + + // - if the element does not generate a principal box (as is the case with 'display: contents' or 'display: none') + // Note: This is the principal box + + // - if its inner display type is 'table' + if (display().is_table_inside()) + return false; + + // - if its principal box is an internal table box + if (display().is_internal_table()) + return false; + + // - if its principal box is an internal ruby box or a non-atomic inline-level box + // FIXME: Implement this. + + if (computed_values().contain().size_containment) + return true; + + return false; +} +// https://drafts.csswg.org/css-contain-2/#containment-inline-size +bool Node::has_inline_size_containment() const +{ + // Giving an element inline-size containment has no effect if any of the following are true: + + // - if the element does not generate a principal box (as is the case with 'display: contents' or 'display: none') + // Note: This is the principal box + + // - if its inner display type is 'table' + if (display().is_table_inside()) + return false; + + // - if its principal box is an internal table box + if (display().is_internal_table()) + return false; + + // - if its principal box is an internal ruby box or a non-atomic inline-level box + // FIXME: Implement this. + + if (computed_values().contain().inline_size_containment) + return true; + + return false; +} +// https://drafts.csswg.org/css-contain-2/#containment-layout +bool Node::has_layout_containment() const +{ + // However, giving an element layout containment has no effect if any of the following are true: + + // - if the element does not generate a principal box (as is the case with 'display: contents' or 'display: none') + // Note: This is the principal box + + // - if its principal box is an internal table box other than 'table-cell' + if (display().is_internal_table() && !display().is_table_cell()) + return false; + + // - if its principal box is an internal ruby box or a non-atomic inline-level box + // FIXME: Implement this. + + if (computed_values().contain().layout_containment) + return true; + + // https://drafts.csswg.org/css-contain-2/#valdef-content-visibility-auto + // Changes the used value of the 'contain' property so as to turn on layout containment, style containment, and + // paint containment for the element. + if (computed_values().content_visibility() == CSS::ContentVisibility::Auto) + return true; + + return false; +} +// https://drafts.csswg.org/css-contain-2/#containment-style +bool Node::has_style_containment() const +{ + // However, giving an element style containment has no effect if any of the following are true: + + // - if the element does not generate a principal box (as is the case with 'display: contents' or 'display: none') + // Note: This is the principal box + + if (computed_values().contain().style_containment) + return true; + + // https://drafts.csswg.org/css-contain-2/#valdef-content-visibility-auto + // Changes the used value of the 'contain' property so as to turn on layout containment, style containment, and + // paint containment for the element. + if (computed_values().content_visibility() == CSS::ContentVisibility::Auto) + return true; + + return false; +} +// https://drafts.csswg.org/css-contain-2/#containment-paint +bool Node::has_paint_containment() const +{ + // However, giving an element paint containment has no effect if any of the following are true: + + // - if the element does not generate a principal box (as is the case with 'display: contents' or 'display: none') + // Note: This is the principal box + + // - if its principal box is an internal table box other than 'table-cell' + if (display().is_internal_table() && !display().is_table_cell()) + return false; + + // - if its principal box is an internal ruby box or a non-atomic inline-level box + // FIXME: Implement this + + if (computed_values().contain().paint_containment) + return true; + + // https://drafts.csswg.org/css-contain-2/#valdef-content-visibility-auto + // Changes the used value of the 'contain' property so as to turn on layout containment, style containment, and + // paint containment for the element. + if (computed_values().content_visibility() == CSS::ContentVisibility::Auto) + return true; + + return false; +} + bool NodeWithStyleAndBoxModelMetrics::should_create_inline_continuation() const { // This node must have an inline parent. diff --git a/Libraries/LibWeb/Layout/Node.h b/Libraries/LibWeb/Layout/Node.h index 67c8d3dfa93..80b85f5f4a2 100644 --- a/Libraries/LibWeb/Layout/Node.h +++ b/Libraries/LibWeb/Layout/Node.h @@ -192,6 +192,13 @@ public: // https://drafts.csswg.org/css-ui/#propdef-user-select CSS::UserSelect user_select_used_value() const; + // https://drafts.csswg.org/css-contain-2/#containment-types + bool has_size_containment() const; + bool has_inline_size_containment() const; + bool has_layout_containment() const; + bool has_style_containment() const; + bool has_paint_containment() const; + [[nodiscard]] bool has_been_wrapped_in_table_wrapper() const { return m_has_been_wrapped_in_table_wrapper; } void set_has_been_wrapped_in_table_wrapper(bool value) { m_has_been_wrapped_in_table_wrapper = value; } diff --git a/Libraries/LibWeb/Layout/TreeBuilder.cpp b/Libraries/LibWeb/Layout/TreeBuilder.cpp index 64ef0436b19..030a7a06582 100644 --- a/Libraries/LibWeb/Layout/TreeBuilder.cpp +++ b/Libraries/LibWeb/Layout/TreeBuilder.cpp @@ -626,7 +626,7 @@ void TreeBuilder::update_layout_tree(DOM::Node& dom_node, TreeBuilder::Context& // Giving an element style containment has the following effects: // 2. The effects of the 'content' property’s 'open-quote', 'close-quote', 'no-open-quote' and 'no-close-quote' must // be scoped to the element’s sub-tree. - if (dom_node.is_element() && (static_cast(dom_node)).has_style_containment()) { + if (layout_node->has_style_containment()) { m_quote_nesting_level = prior_quote_nesting_level; } diff --git a/Libraries/LibWeb/Painting/ViewportPaintable.cpp b/Libraries/LibWeb/Painting/ViewportPaintable.cpp index f61012956a5..e80f344bca4 100644 --- a/Libraries/LibWeb/Painting/ViewportPaintable.cpp +++ b/Libraries/LibWeb/Painting/ViewportPaintable.cpp @@ -129,8 +129,7 @@ void ViewportPaintable::assign_clip_frames() auto overflow_y = paintable_box.computed_values().overflow_y(); // Note: Overflow may be clip on one axis and visible on the other. auto has_hidden_overflow = overflow_x != CSS::Overflow::Visible || overflow_y != CSS::Overflow::Visible; - auto element = as_if(paintable_box.layout_node().dom_node()); - if (has_hidden_overflow || paintable_box.get_clip_rect().has_value() || (element && element->has_paint_containment())) { + if (has_hidden_overflow || paintable_box.get_clip_rect().has_value() || paintable_box.layout_node().has_paint_containment()) { auto clip_frame = adopt_ref(*new ClipFrame()); clip_state.set(paintable_box, move(clip_frame)); } @@ -185,14 +184,12 @@ void ViewportPaintable::assign_clip_frames() // any such mechanism through other properties, such as overflow, resize, or text-overflow. // NOTE: This clipping shape respects overflow-clip-margin, allowing an element with paint containment // to still slightly overflow its normal bounds. - if (auto element = as_if(block->dom_node())) { - if (element->has_paint_containment()) { - // NOTE: Note: The behavior is described in this paragraph is equivalent to changing 'overflow-x: visible' into - // 'overflow-x: clip' and 'overflow-y: visible' into 'overflow-y: clip' at used value time, while leaving other - // values of 'overflow-x' and 'overflow-y' unchanged. - clip_x = true; - clip_y = true; - } + if (block->has_paint_containment()) { + // NOTE: Note: The behavior is described in this paragraph is equivalent to changing 'overflow-x: visible' into + // 'overflow-x: clip' and 'overflow-y: visible' into 'overflow-y: clip' at used value time, while leaving other + // values of 'overflow-x' and 'overflow-y' unchanged. + clip_x = true; + clip_y = true; } if (clip_x || clip_y) {