diff --git a/Libraries/LibWeb/CSS/Selector.cpp b/Libraries/LibWeb/CSS/Selector.cpp index f8fc177db9b..ea887630363 100644 --- a/Libraries/LibWeb/CSS/Selector.cpp +++ b/Libraries/LibWeb/CSS/Selector.cpp @@ -765,6 +765,36 @@ Optional Selector::SimpleSelector::absolutized(Selecto VERIFY_NOT_REACHED(); } +size_t Selector::sibling_invalidation_distance() const +{ + if (m_sibling_invalidation_distance.has_value()) + return *m_sibling_invalidation_distance; + + m_sibling_invalidation_distance = 0; + size_t current_distance = 0; + for (auto const& compound_selector : compound_selectors()) { + if (compound_selector.combinator == Combinator::None) + continue; + + if (compound_selector.combinator == Combinator::SubsequentSibling) { + m_sibling_invalidation_distance = NumericLimits::max(); + return *m_sibling_invalidation_distance; + } + + if (compound_selector.combinator == Combinator::NextSibling) { + current_distance++; + } else { + m_sibling_invalidation_distance = max(*m_sibling_invalidation_distance, current_distance); + current_distance = 0; + } + } + + if (current_distance > 0) { + m_sibling_invalidation_distance = max(*m_sibling_invalidation_distance, current_distance); + } + return *m_sibling_invalidation_distance; +} + SelectorList adapt_nested_relative_selector_list(SelectorList const& selectors) { // "Nested style rules differ from non-nested rules in the following ways: diff --git a/Libraries/LibWeb/CSS/Selector.h b/Libraries/LibWeb/CSS/Selector.h index a0b9bbd509f..49e218e0a57 100644 --- a/Libraries/LibWeb/CSS/Selector.h +++ b/Libraries/LibWeb/CSS/Selector.h @@ -271,12 +271,15 @@ public: bool can_use_fast_matches() const { return m_can_use_fast_matches; } bool can_use_ancestor_filter() const { return m_can_use_ancestor_filter; } + size_t sibling_invalidation_distance() const; + private: explicit Selector(Vector&&); Vector m_compound_selectors; mutable Optional m_specificity; Optional m_pseudo_element; + mutable Optional m_sibling_invalidation_distance; bool m_can_use_fast_matches { false }; bool m_can_use_ancestor_filter { false }; bool m_contains_the_nesting_selector { false }; diff --git a/Libraries/LibWeb/CSS/SelectorEngine.cpp b/Libraries/LibWeb/CSS/SelectorEngine.cpp index 961eb0f7956..ebffb46a715 100644 --- a/Libraries/LibWeb/CSS/SelectorEngine.cpp +++ b/Libraries/LibWeb/CSS/SelectorEngine.cpp @@ -978,7 +978,9 @@ bool matches(CSS::Selector const& selector, int component_list_index, DOM::Eleme } case CSS::Selector::Combinator::NextSibling: if (context.collect_per_element_selector_involvement_metadata) { - const_cast(element).set_affected_by_sibling_combinator(true); + const_cast(element).set_affected_by_direct_sibling_combinator(true); + auto new_sibling_invalidation_distance = max(selector.sibling_invalidation_distance(), element.sibling_invalidation_distance()); + const_cast(element).set_sibling_invalidation_distance(new_sibling_invalidation_distance); } VERIFY(component_list_index != 0); if (auto* sibling = element.previous_element_sibling()) @@ -986,7 +988,7 @@ bool matches(CSS::Selector const& selector, int component_list_index, DOM::Eleme return false; case CSS::Selector::Combinator::SubsequentSibling: if (context.collect_per_element_selector_involvement_metadata) { - const_cast(element).set_affected_by_sibling_combinator(true); + const_cast(element).set_affected_by_indirect_sibling_combinator(true); } VERIFY(component_list_index != 0); for (auto* sibling = element.previous_element_sibling(); sibling; sibling = sibling->previous_element_sibling()) { diff --git a/Libraries/LibWeb/DOM/Element.cpp b/Libraries/LibWeb/DOM/Element.cpp index 92600d89849..0ef76abf31a 100644 --- a/Libraries/LibWeb/DOM/Element.cpp +++ b/Libraries/LibWeb/DOM/Element.cpp @@ -509,9 +509,11 @@ CSS::RequiredInvalidationAfterStyleChange Element::recompute_style() m_affected_by_has_pseudo_class_in_subject_position = false; m_affected_by_has_pseudo_class_in_non_subject_position = false; m_affected_by_has_pseudo_class_with_relative_selector_that_has_sibling_combinator = false; - m_affected_by_sibling_combinator = false; + m_affected_by_direct_sibling_combinator = false; + m_affected_by_indirect_sibling_combinator = false; m_affected_by_first_or_last_child_pseudo_class = false; m_affected_by_nth_child_pseudo_class = false; + m_sibling_invalidation_distance = 0; auto& style_computer = document().style_computer(); auto new_computed_properties = style_computer.compute_style(*this); diff --git a/Libraries/LibWeb/DOM/Element.h b/Libraries/LibWeb/DOM/Element.h index 4ab4f93be51..29bc2052a74 100644 --- a/Libraries/LibWeb/DOM/Element.h +++ b/Libraries/LibWeb/DOM/Element.h @@ -437,8 +437,11 @@ public: bool affected_by_has_pseudo_class_with_relative_selector_that_has_sibling_combinator() const { return m_affected_by_has_pseudo_class_with_relative_selector_that_has_sibling_combinator; } void set_affected_by_has_pseudo_class_with_relative_selector_that_has_sibling_combinator(bool value) { m_affected_by_has_pseudo_class_with_relative_selector_that_has_sibling_combinator = value; } - bool affected_by_sibling_combinator() const { return m_affected_by_sibling_combinator; } - void set_affected_by_sibling_combinator(bool value) { m_affected_by_sibling_combinator = value; } + bool affected_by_direct_sibling_combinator() const { return m_affected_by_direct_sibling_combinator; } + void set_affected_by_direct_sibling_combinator(bool value) { m_affected_by_direct_sibling_combinator = value; } + + bool affected_by_indirect_sibling_combinator() const { return m_affected_by_indirect_sibling_combinator; } + void set_affected_by_indirect_sibling_combinator(bool value) { m_affected_by_indirect_sibling_combinator = value; } bool affected_by_first_or_last_child_pseudo_class() const { return m_affected_by_first_or_last_child_pseudo_class; } void set_affected_by_first_or_last_child_pseudo_class(bool value) { m_affected_by_first_or_last_child_pseudo_class = value; } @@ -446,9 +449,12 @@ public: bool affected_by_nth_child_pseudo_class() const { return m_affected_by_nth_child_pseudo_class; } void set_affected_by_nth_child_pseudo_class(bool value) { m_affected_by_nth_child_pseudo_class = value; } + size_t sibling_invalidation_distance() const { return m_sibling_invalidation_distance; } + void set_sibling_invalidation_distance(size_t value) { m_sibling_invalidation_distance = value; } + bool style_affected_by_structural_changes() const { - return affected_by_sibling_combinator() || affected_by_first_or_last_child_pseudo_class() || affected_by_nth_child_pseudo_class(); + return affected_by_direct_sibling_combinator() || affected_by_indirect_sibling_combinator() || affected_by_first_or_last_child_pseudo_class() || affected_by_nth_child_pseudo_class(); } size_t number_of_owned_list_items() const; @@ -547,11 +553,14 @@ private: bool m_style_uses_css_custom_properties { false }; bool m_affected_by_has_pseudo_class_in_subject_position : 1 { false }; bool m_affected_by_has_pseudo_class_in_non_subject_position : 1 { false }; - bool m_affected_by_sibling_combinator : 1 { false }; + bool m_affected_by_direct_sibling_combinator : 1 { false }; + bool m_affected_by_indirect_sibling_combinator : 1 { false }; bool m_affected_by_first_or_last_child_pseudo_class : 1 { false }; bool m_affected_by_nth_child_pseudo_class : 1 { false }; bool m_affected_by_has_pseudo_class_with_relative_selector_that_has_sibling_combinator : 1 { false }; + size_t m_sibling_invalidation_distance { 0 }; + OwnPtr m_counters_set; GC::Ptr m_aria_active_descendant_element; diff --git a/Libraries/LibWeb/DOM/Node.cpp b/Libraries/LibWeb/DOM/Node.cpp index 819d849674c..d2d9b440814 100644 --- a/Libraries/LibWeb/DOM/Node.cpp +++ b/Libraries/LibWeb/DOM/Node.cpp @@ -460,16 +460,21 @@ void Node::invalidate_style(StyleInvalidationReason reason) } } + size_t current_sibling_distance = 1; for (auto* sibling = next_sibling(); sibling; sibling = sibling->next_sibling()) { if (auto* element = as_if(sibling)) { bool needs_to_invalidate = false; if (reason == StyleInvalidationReason::NodeInsertBefore || reason == StyleInvalidationReason::NodeRemove) { needs_to_invalidate = element->style_affected_by_structural_changes(); - } else { - needs_to_invalidate = element->affected_by_sibling_combinator() || element->affected_by_nth_child_pseudo_class(); + } else if (element->affected_by_indirect_sibling_combinator() || element->affected_by_nth_child_pseudo_class()) { + needs_to_invalidate = true; + } else if (element->affected_by_direct_sibling_combinator() && current_sibling_distance <= element->sibling_invalidation_distance()) { + needs_to_invalidate = true; } - if (needs_to_invalidate) + if (needs_to_invalidate) { element->set_entire_subtree_needs_style_update(true); + } + current_sibling_distance++; } }