LibWeb: Optimize :hover style invalidation

Instead of recalculating styles for all nodes in the common ancestor of
the new and old hovered nodes' subtrees, this change introduces the
following approach:
- While calculating ComputedProperties, a flag is saved if any rule
  applied to an element is affected by the hover state during the
  execution of SelectorEngine::matches().
- When the hovered element changes, styles are marked for recalculation
  only if the flag saved in ComputedProperties indicates that the
  element could be affected by the hover state.
This commit is contained in:
Aliaksandr Kalenik 2025-01-03 20:39:25 +03:00 committed by Andreas Kling
commit e465e922bd
Notes: github-actions[bot] 2025-01-04 19:33:47 +00:00
9 changed files with 124 additions and 77 deletions

View file

@ -742,7 +742,8 @@ WebIDL::ExceptionOr<bool> Element::matches(StringView selectors) const
// 3. If the result of match a selector against an element, using s, this, and scoping root this, returns success, then return true; otherwise, return false.
auto sel = maybe_selectors.value();
for (auto& s : sel) {
if (SelectorEngine::matches(s, {}, *this, nullptr, {}, static_cast<ParentNode const*>(this)))
SelectorEngine::MatchContext context;
if (SelectorEngine::matches(s, *this, nullptr, context, {}, static_cast<ParentNode const*>(this)))
return true;
}
return false;
@ -761,7 +762,8 @@ WebIDL::ExceptionOr<DOM::Element const*> Element::closest(StringView selectors)
auto matches_selectors = [this](CSS::SelectorList const& selector_list, Element const* element) {
// 4. For each element in elements, if match a selector against an element, using s, element, and scoping root this, returns success, return element.
for (auto const& selector : selector_list) {
if (SelectorEngine::matches(selector, {}, *element, nullptr, {}, this))
SelectorEngine::MatchContext context;
if (SelectorEngine::matches(selector, *element, nullptr, context, {}, this))
return true;
}
return false;
@ -1123,6 +1125,22 @@ GC::Ptr<Layout::NodeWithStyle> Element::get_pseudo_element_node(CSS::Selector::P
return nullptr;
}
bool Element::affected_by_hover() const
{
if (m_computed_properties && m_computed_properties->did_match_any_hover_rules()) {
return true;
}
if (m_pseudo_element_data) {
for (auto& pseudo_element : *m_pseudo_element_data) {
if (!pseudo_element.computed_properties)
continue;
if (pseudo_element.computed_properties->did_match_any_hover_rules())
return true;
}
}
return false;
}
bool Element::has_pseudo_elements() const
{
if (m_pseudo_element_data) {