From 90ba4b16c24acb7d8a966022837ef79fa04e7787 Mon Sep 17 00:00:00 2001 From: Aliaksandr Kalenik Date: Mon, 10 Feb 2025 18:57:05 +0100 Subject: [PATCH] LibWeb: Postpone `:has()` style invalidation until update_style() This allows to do ancestors traversal only once even if `invalidate_style()` was called multiple times for the same node. --- Libraries/LibWeb/DOM/Document.cpp | 15 +++++++++++---- Libraries/LibWeb/DOM/Document.h | 13 ++++++++----- Libraries/LibWeb/DOM/Node.cpp | 21 +++++++++++++++++---- 3 files changed, 36 insertions(+), 13 deletions(-) diff --git a/Libraries/LibWeb/DOM/Document.cpp b/Libraries/LibWeb/DOM/Document.cpp index db2863f78ed..40f5eceeea6 100644 --- a/Libraries/LibWeb/DOM/Document.cpp +++ b/Libraries/LibWeb/DOM/Document.cpp @@ -1438,7 +1438,7 @@ void Document::update_style() // style change event. [CSS-Transitions-2] m_transition_generation++; - invalidate_elements_affected_by_has_in_non_subject_position(); + invalidate_style_of_elements_affected_by_has(); if (!needs_full_style_update() && !needs_style_update() && !child_needs_style_update()) return; @@ -1659,11 +1659,18 @@ static Node* find_common_ancestor(Node* a, Node* b) return nullptr; } -void Document::invalidate_elements_affected_by_has_in_non_subject_position() +void Document::invalidate_style_of_elements_affected_by_has() { - if (!m_needs_invalidate_elements_affected_by_has_in_non_subject_position) + if (m_pending_nodes_for_style_invalidation_due_to_presence_of_has.is_empty()) { return; - m_needs_invalidate_elements_affected_by_has_in_non_subject_position = false; + } + + for (auto const& node : m_pending_nodes_for_style_invalidation_due_to_presence_of_has) { + if (node.is_null()) + continue; + node->invalidate_ancestors_affected_by_has_in_subject_position(); + } + m_pending_nodes_for_style_invalidation_due_to_presence_of_has.clear(); // Take care of elements that affected by :has() in non-subject position, i.e., ".a:has(.b) > .c". // Elements affected by :has() in subject position, i.e., ".a:has(.b)", are handled by diff --git a/Libraries/LibWeb/DOM/Document.h b/Libraries/LibWeb/DOM/Document.h index e01e00a1d3b..5abb6567dd2 100644 --- a/Libraries/LibWeb/DOM/Document.h +++ b/Libraries/LibWeb/DOM/Document.h @@ -510,9 +510,6 @@ public: [[nodiscard]] bool needs_full_layout_tree_update() const { return m_needs_full_layout_tree_update; } void set_needs_full_layout_tree_update(bool b) { m_needs_full_layout_tree_update = b; } - bool needs_invalidate_elements_affected_by_has_in_non_subject_position() const { return m_needs_invalidate_elements_affected_by_has_in_non_subject_position; } - void set_needs_invalidate_elements_affected_by_has_in_non_subject_position(bool b) { m_needs_invalidate_elements_affected_by_has_in_non_subject_position = b; } - void set_needs_to_refresh_scroll_state(bool b); bool has_active_favicon() const { return m_active_favicon; } @@ -807,6 +804,11 @@ public: void add_render_blocking_element(GC::Ref); void remove_render_blocking_element(GC::Ref); + void schedule_ancestors_style_invalidation_due_to_presence_of_has(Node& node) + { + m_pending_nodes_for_style_invalidation_due_to_presence_of_has.set(node.make_weak_ptr()); + } + protected: virtual void initialize(JS::Realm&) override; virtual void visit_edges(Cell::Visitor&) override; @@ -817,7 +819,7 @@ private: // ^HTML::GlobalEventHandlers virtual GC::Ptr global_event_handlers_to_event_target(FlyString const&) final { return *this; } - void invalidate_elements_affected_by_has_in_non_subject_position(); + void invalidate_style_of_elements_affected_by_has(); void tear_down_layout_tree(); @@ -961,7 +963,6 @@ private: bool m_needs_full_style_update { false }; bool m_needs_full_layout_tree_update { false }; - bool m_needs_invalidate_elements_affected_by_has_in_non_subject_position { false }; bool m_needs_animated_style_update { false }; @@ -1146,6 +1147,8 @@ private: // https://html.spec.whatwg.org/multipage/dom.html#render-blocking-element-set HashTable> m_render_blocking_elements; + + HashTable> m_pending_nodes_for_style_invalidation_due_to_presence_of_has; }; template<> diff --git a/Libraries/LibWeb/DOM/Node.cpp b/Libraries/LibWeb/DOM/Node.cpp index 677008fc022..27249648d46 100644 --- a/Libraries/LibWeb/DOM/Node.cpp +++ b/Libraries/LibWeb/DOM/Node.cpp @@ -430,8 +430,22 @@ void Node::invalidate_style(StyleInvalidationReason reason) return; if (document().style_computer().may_have_has_selectors()) { - invalidate_ancestors_affected_by_has_in_subject_position(); - document().set_needs_invalidate_elements_affected_by_has_in_non_subject_position(true); + if (reason == StyleInvalidationReason::NodeRemove) { + for (auto* previous_sibling = this->previous_sibling(); previous_sibling; previous_sibling = previous_sibling->previous_sibling()) { + if (!previous_sibling->is_element()) + continue; + auto& element = static_cast(*previous_sibling); + if (element.affected_by_has_pseudo_class_with_relative_selector_that_has_sibling_combinator()) { + element.set_needs_style_update(true); + } + } + + if (auto parent = parent_or_shadow_host(); parent) { + document().schedule_ancestors_style_invalidation_due_to_presence_of_has(*parent); + } + } else { + document().schedule_ancestors_style_invalidation_due_to_presence_of_has(*this); + } } if (!needs_style_update() && !document().needs_full_style_update()) { @@ -493,8 +507,7 @@ void Node::invalidate_style(StyleInvalidationReason, Vector