diff --git a/Libraries/LibWeb/CSS/ComputedProperties.h b/Libraries/LibWeb/CSS/ComputedProperties.h index fcf0dbf04ad..8614bbb666f 100644 --- a/Libraries/LibWeb/CSS/ComputedProperties.h +++ b/Libraries/LibWeb/CSS/ComputedProperties.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2024, Andreas Kling + * Copyright (c) 2018-2025, Andreas Kling * Copyright (c) 2023-2025, Sam Atkins * * SPDX-License-Identifier: BSD-2-Clause @@ -18,6 +18,8 @@ #include #include #include +#include +#include #include namespace Web::CSS { @@ -228,8 +230,15 @@ public: static float resolve_opacity_value(CSSStyleValue const& value); - bool did_match_any_hover_rules() const { return m_did_match_any_hover_rules; } - void set_did_match_any_hover_rules() { m_did_match_any_hover_rules = true; } + bool has_attempted_match_against_pseudo_class(PseudoClass pseudo_class) const + { + return m_attempted_pseudo_class_matches.get(pseudo_class); + } + + void set_attempted_pseudo_class_matches(PseudoClassBitmap const& results) + { + m_attempted_pseudo_class_matches = results; + } private: friend class StyleComputer; @@ -256,7 +265,7 @@ private: Optional m_line_height; - bool m_did_match_any_hover_rules { false }; + PseudoClassBitmap m_attempted_pseudo_class_matches; }; } diff --git a/Libraries/LibWeb/CSS/PseudoClassBitmap.h b/Libraries/LibWeb/CSS/PseudoClassBitmap.h new file mode 100644 index 00000000000..025a0b710fb --- /dev/null +++ b/Libraries/LibWeb/CSS/PseudoClassBitmap.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2025, Andreas Kling + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include + +namespace Web::CSS { + +class PseudoClassBitmap { +public: + PseudoClassBitmap() = default; + ~PseudoClassBitmap() = default; + + void set(PseudoClass pseudo_class, bool bit) + { + size_t const index = to_underlying(pseudo_class); + if (bit) + m_bits |= 1LLU << index; + else + m_bits &= ~(1LLU << index); + } + + bool get(PseudoClass pseudo_class) const + { + size_t const index = to_underlying(pseudo_class); + return (m_bits & (1LLU << index)) != 0; + } + + void operator|=(PseudoClassBitmap const& other) + { + m_bits |= other.m_bits; + } + +private: + u64 m_bits { 0 }; +}; + +// NOTE: If this changes, we'll have to tweak PseudoClassBitmap a little bit :) +static_assert(to_underlying(PseudoClass::__Count) <= 64); + +} diff --git a/Libraries/LibWeb/CSS/Selector.cpp b/Libraries/LibWeb/CSS/Selector.cpp index d055a706261..d0dc85681cb 100644 --- a/Libraries/LibWeb/CSS/Selector.cpp +++ b/Libraries/LibWeb/CSS/Selector.cpp @@ -101,16 +101,12 @@ Selector::Selector(Vector&& compound_selectors) break; } if (simple_selector.type == SimpleSelector::Type::PseudoClass) { - if (simple_selector.pseudo_class().type == PseudoClass::Hover) { - m_contains_hover_pseudo_class = true; - } + m_contained_pseudo_classes.set(simple_selector.pseudo_class().type, true); for (auto const& child_selector : simple_selector.pseudo_class().argument_selector_list) { if (child_selector->contains_the_nesting_selector()) { m_contains_the_nesting_selector = true; } - if (child_selector->contains_hover_pseudo_class()) { - m_contains_hover_pseudo_class = true; - } + m_contained_pseudo_classes |= child_selector->m_contained_pseudo_classes; } if (m_contains_the_nesting_selector) break; diff --git a/Libraries/LibWeb/CSS/Selector.h b/Libraries/LibWeb/CSS/Selector.h index ac206fc07eb..938fdb0a07d 100644 --- a/Libraries/LibWeb/CSS/Selector.h +++ b/Libraries/LibWeb/CSS/Selector.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2020, Andreas Kling + * Copyright (c) 2018-2025, Andreas Kling * Copyright (c) 2021-2025, Sam Atkins * * SPDX-License-Identifier: BSD-2-Clause @@ -14,6 +14,7 @@ #include #include #include +#include #include namespace Web::CSS { @@ -236,7 +237,7 @@ public: Optional const& pseudo_element() const { return m_pseudo_element; } NonnullRefPtr relative_to(SimpleSelector const&) const; bool contains_the_nesting_selector() const { return m_contains_the_nesting_selector; } - bool contains_hover_pseudo_class() const { return m_contains_hover_pseudo_class; } + bool contains_pseudo_class(PseudoClass pseudo_class) const { return m_contained_pseudo_classes.get(pseudo_class); } bool contains_unknown_webkit_pseudo_element() const; RefPtr absolutized(SimpleSelector const& selector_for_nesting) const; u32 specificity() const; @@ -259,7 +260,8 @@ private: bool m_can_use_fast_matches { false }; bool m_can_use_ancestor_filter { false }; bool m_contains_the_nesting_selector { false }; - bool m_contains_hover_pseudo_class { false }; + + PseudoClassBitmap m_contained_pseudo_classes; void collect_ancestor_hashes(); diff --git a/Libraries/LibWeb/CSS/SelectorEngine.cpp b/Libraries/LibWeb/CSS/SelectorEngine.cpp index 5c8d5eabb31..bda666c9f86 100644 --- a/Libraries/LibWeb/CSS/SelectorEngine.cpp +++ b/Libraries/LibWeb/CSS/SelectorEngine.cpp @@ -433,7 +433,10 @@ static bool matches_optimal_value_pseudo_class(DOM::Element const& element, HTML static inline bool matches_pseudo_class(CSS::Selector::SimpleSelector::PseudoClassSelector const& pseudo_class, DOM::Element const& element, GC::Ptr shadow_host, MatchContext& context, GC::Ptr scope, SelectorKind selector_kind) { + context.attempted_pseudo_class_matches.set(pseudo_class.type, true); switch (pseudo_class.type) { + case CSS::PseudoClass::__Count: + VERIFY_NOT_REACHED(); case CSS::PseudoClass::Link: case CSS::PseudoClass::AnyLink: // NOTE: AnyLink should match whether the link is visited or not, so if we ever start matching @@ -448,7 +451,6 @@ static inline bool matches_pseudo_class(CSS::Selector::SimpleSelector::PseudoCla case CSS::PseudoClass::Active: return element.is_active(); case CSS::PseudoClass::Hover: - context.did_match_any_hover_rules = true; return matches_hover_pseudo_class(element); case CSS::PseudoClass::Focus: return element.is_focused(); @@ -586,6 +588,8 @@ static inline bool matches_pseudo_class(CSS::Selector::SimpleSelector::PseudoCla int index = 1; switch (pseudo_class.type) { + case CSS::PseudoClass::__Count: + VERIFY_NOT_REACHED(); case CSS::PseudoClass::NthChild: { if (!matches_selector_list(pseudo_class.argument_selector_list, element)) return false; diff --git a/Libraries/LibWeb/CSS/SelectorEngine.h b/Libraries/LibWeb/CSS/SelectorEngine.h index 7c45c67d3e7..e575d1dd53c 100644 --- a/Libraries/LibWeb/CSS/SelectorEngine.h +++ b/Libraries/LibWeb/CSS/SelectorEngine.h @@ -20,7 +20,7 @@ struct MatchContext { GC::Ptr style_sheet_for_rule {}; GC::Ptr subject {}; bool collect_per_element_selector_involvement_metadata { false }; - bool did_match_any_hover_rules { false }; + CSS::PseudoClassBitmap attempted_pseudo_class_matches {}; }; bool matches(CSS::Selector const&, DOM::Element const&, GC::Ptr shadow_host, MatchContext& context, Optional = {}, GC::Ptr scope = {}, SelectorKind selector_kind = SelectorKind::Normal, GC::Ptr anchor = nullptr); diff --git a/Libraries/LibWeb/CSS/StyleComputer.cpp b/Libraries/LibWeb/CSS/StyleComputer.cpp index 39e4bbf5c3e..f3b095c86ba 100644 --- a/Libraries/LibWeb/CSS/StyleComputer.cpp +++ b/Libraries/LibWeb/CSS/StyleComputer.cpp @@ -422,10 +422,10 @@ RuleCache const* StyleComputer::rule_cache_for_cascade_origin(CascadeOrigin casc return true; } -RuleCache const& StyleComputer::get_hover_rules() const +RuleCache const& StyleComputer::get_pseudo_class_rule_cache(PseudoClass pseudo_class) const { build_rule_cache_if_needed(); - return *m_hover_rule_cache; + return *m_pseudo_class_rule_cache[to_underlying(pseudo_class)]; } InvalidationSet StyleComputer::invalidation_set_for_properties(Vector const& properties) const @@ -472,7 +472,7 @@ bool StyleComputer::invalidation_property_used_in_has_selector(InvalidationSet:: return false; } -Vector StyleComputer::collect_matching_rules(DOM::Element const& element, CascadeOrigin cascade_origin, Optional pseudo_element, bool& did_match_any_hover_rules, FlyString const& qualified_layer_name) const +Vector StyleComputer::collect_matching_rules(DOM::Element const& element, CascadeOrigin cascade_origin, Optional pseudo_element, PseudoClassBitmap& attempted_pseudo_class_matches, FlyString const& qualified_layer_name) const { auto const& root_node = element.root(); auto shadow_root = is(root_node) ? static_cast(&root_node) : nullptr; @@ -567,8 +567,7 @@ Vector StyleComputer::collect_matching_rules(DOM::Element c .collect_per_element_selector_involvement_metadata = true, }; ScopeGuard guard = [&] { - if (context.did_match_any_hover_rules) - did_match_any_hover_rules = true; + attempted_pseudo_class_matches |= context.attempted_pseudo_class_matches; }; if (!SelectorEngine::matches(selector, element, shadow_host_to_use, context, pseudo_element)) continue; @@ -1566,24 +1565,24 @@ void StyleComputer::start_needed_transitions(ComputedProperties const& previous_ // https://www.w3.org/TR/css-cascade/#cascading // https://drafts.csswg.org/css-cascade-5/#layering -GC::Ref StyleComputer::compute_cascaded_values(DOM::Element& element, Optional pseudo_element, bool& did_match_any_pseudo_element_rules, bool& did_match_any_hover_rules, ComputeStyleMode mode) const +GC::Ref StyleComputer::compute_cascaded_values(DOM::Element& element, Optional pseudo_element, bool& did_match_any_pseudo_element_rules, PseudoClassBitmap& attempted_pseudo_class_matches, ComputeStyleMode mode) const { auto cascaded_properties = m_document->heap().allocate(); // First, we collect all the CSS rules whose selectors match `element`: MatchingRuleSet matching_rule_set; - matching_rule_set.user_agent_rules = collect_matching_rules(element, CascadeOrigin::UserAgent, pseudo_element, did_match_any_hover_rules); + matching_rule_set.user_agent_rules = collect_matching_rules(element, CascadeOrigin::UserAgent, pseudo_element, attempted_pseudo_class_matches); sort_matching_rules(matching_rule_set.user_agent_rules); - matching_rule_set.user_rules = collect_matching_rules(element, CascadeOrigin::User, pseudo_element, did_match_any_hover_rules); + matching_rule_set.user_rules = collect_matching_rules(element, CascadeOrigin::User, pseudo_element, attempted_pseudo_class_matches); sort_matching_rules(matching_rule_set.user_rules); // @layer-ed author rules for (auto const& layer_name : m_qualified_layer_names_in_order) { - auto layer_rules = collect_matching_rules(element, CascadeOrigin::Author, pseudo_element, did_match_any_hover_rules, layer_name); + auto layer_rules = collect_matching_rules(element, CascadeOrigin::Author, pseudo_element, attempted_pseudo_class_matches, layer_name); sort_matching_rules(layer_rules); matching_rule_set.author_rules.append({ layer_name, layer_rules }); } // Un-@layer-ed author rules - auto unlayered_author_rules = collect_matching_rules(element, CascadeOrigin::Author, pseudo_element, did_match_any_hover_rules); + auto unlayered_author_rules = collect_matching_rules(element, CascadeOrigin::Author, pseudo_element, attempted_pseudo_class_matches); sort_matching_rules(unlayered_author_rules); matching_rule_set.author_rules.append({ {}, unlayered_author_rules }); @@ -2427,8 +2426,8 @@ GC::Ptr StyleComputer::compute_style_impl(DOM::Element& elem // 1. Perform the cascade. This produces the "specified style" bool did_match_any_pseudo_element_rules = false; - bool did_match_any_hover_rules = false; - auto cascaded_properties = compute_cascaded_values(element, pseudo_element, did_match_any_pseudo_element_rules, did_match_any_hover_rules, mode); + PseudoClassBitmap attempted_pseudo_class_matches; + auto cascaded_properties = compute_cascaded_values(element, pseudo_element, did_match_any_pseudo_element_rules, attempted_pseudo_class_matches, mode); element.set_cascaded_properties(pseudo_element, cascaded_properties); @@ -2462,8 +2461,7 @@ GC::Ptr StyleComputer::compute_style_impl(DOM::Element& elem } auto computed_properties = compute_properties(element, pseudo_element, cascaded_properties); - if (did_match_any_hover_rules) - computed_properties->set_did_match_any_hover_rules(); + computed_properties->set_attempted_pseudo_class_matches(attempted_pseudo_class_matches); return computed_properties; } @@ -2834,10 +2832,17 @@ void StyleComputer::make_rule_cache_for_cascade_origin(CascadeOrigin cascade_ori } } - if (selector.contains_hover_pseudo_class()) { - // For hover rule cache we intentionally pass pseudo_element as None, because we don't want to bucket hover rules by pseudo element type - m_hover_rule_cache->add_rule(matching_rule, {}, contains_root_pseudo_class); + for (size_t i = 0; i < to_underlying(PseudoClass::__Count); ++i) { + auto pseudo_class = static_cast(i); + // If we're not building a rule cache for this pseudo class, just ignore it. + if (!m_pseudo_class_rule_cache[i]) + continue; + if (selector.contains_pseudo_class(pseudo_class)) { + // For pseudo class rule caches we intentionally pass no pseudo-element, because we don't want to bucket pseudo class rules by pseudo-element type. + m_pseudo_class_rule_cache[i]->add_rule(matching_rule, {}, contains_root_pseudo_class); + } } + rule_cache.add_rule(matching_rule, pseudo_element, contains_root_pseudo_class); } ++rule_index; @@ -2965,7 +2970,14 @@ void StyleComputer::build_rule_cache() build_qualified_layer_names_cache(); - m_hover_rule_cache = make(); + m_pseudo_class_rule_cache[to_underlying(PseudoClass::Hover)] = make(); + m_pseudo_class_rule_cache[to_underlying(PseudoClass::Active)] = make(); + m_pseudo_class_rule_cache[to_underlying(PseudoClass::Focus)] = make(); + m_pseudo_class_rule_cache[to_underlying(PseudoClass::FocusWithin)] = make(); + m_pseudo_class_rule_cache[to_underlying(PseudoClass::FocusVisible)] = make(); + m_pseudo_class_rule_cache[to_underlying(PseudoClass::Target)] = make(); + m_pseudo_class_rule_cache[to_underlying(PseudoClass::TargetWithin)] = make(); + make_rule_cache_for_cascade_origin(CascadeOrigin::Author, *m_selector_insights); make_rule_cache_for_cascade_origin(CascadeOrigin::User, *m_selector_insights); make_rule_cache_for_cascade_origin(CascadeOrigin::UserAgent, *m_selector_insights); @@ -2985,7 +2997,7 @@ void StyleComputer::invalidate_rule_cache() // If we are sure that it's safe, we could keep it as an optimization. m_user_agent_rule_cache = nullptr; - m_hover_rule_cache = nullptr; + m_pseudo_class_rule_cache = {}; m_style_invalidation_data = nullptr; } diff --git a/Libraries/LibWeb/CSS/StyleComputer.h b/Libraries/LibWeb/CSS/StyleComputer.h index cf6d7feb479..e76e6c5b177 100644 --- a/Libraries/LibWeb/CSS/StyleComputer.h +++ b/Libraries/LibWeb/CSS/StyleComputer.h @@ -160,8 +160,9 @@ public: [[nodiscard]] GC::Ref compute_style(DOM::Element&, Optional = {}) const; [[nodiscard]] GC::Ptr compute_pseudo_element_style_if_needed(DOM::Element&, Optional) const; - RuleCache const& get_hover_rules() const; - [[nodiscard]] Vector collect_matching_rules(DOM::Element const&, CascadeOrigin, Optional, bool& did_match_any_hover_rules, FlyString const& qualified_layer_name = {}) const; + [[nodiscard]] RuleCache const& get_pseudo_class_rule_cache(PseudoClass) const; + + [[nodiscard]] Vector collect_matching_rules(DOM::Element const&, CascadeOrigin, Optional, PseudoClassBitmap& attempted_psuedo_class_matches, FlyString const& qualified_layer_name = {}) const; InvalidationSet invalidation_set_for_properties(Vector const&) const; bool invalidation_property_used_in_has_selector(InvalidationSet::Property const&) const; @@ -213,7 +214,7 @@ private: struct MatchingFontCandidate; [[nodiscard]] GC::Ptr compute_style_impl(DOM::Element&, Optional, ComputeStyleMode) const; - [[nodiscard]] GC::Ref compute_cascaded_values(DOM::Element&, Optional, bool& did_match_any_pseudo_element_rules, bool& did_match_any_hover_rules, ComputeStyleMode) const; + [[nodiscard]] GC::Ref compute_cascaded_values(DOM::Element&, Optional, bool& did_match_any_pseudo_element_rules, PseudoClassBitmap& attempted_pseudo_class_matches, ComputeStyleMode) const; static RefPtr find_matching_font_weight_ascending(Vector const& candidates, int target_weight, float font_size_in_pt, bool inclusive); static RefPtr find_matching_font_weight_descending(Vector const& candidates, int target_weight, float font_size_in_pt, bool inclusive); RefPtr font_matching_algorithm(FlyString const& family_name, int weight, int slope, float font_size_in_pt) const; @@ -292,7 +293,7 @@ private: static void collect_selector_insights(Selector const&, SelectorInsights&); OwnPtr m_selector_insights; - OwnPtr m_hover_rule_cache; + Array, to_underlying(PseudoClass::__Count)> m_pseudo_class_rule_cache; OwnPtr m_style_invalidation_data; OwnPtr m_author_rule_cache; OwnPtr m_user_rule_cache; diff --git a/Libraries/LibWeb/DOM/Document.cpp b/Libraries/LibWeb/DOM/Document.cpp index bbcd8936da3..65521843b3a 100644 --- a/Libraries/LibWeb/DOM/Document.cpp +++ b/Libraries/LibWeb/DOM/Document.cpp @@ -1779,11 +1779,11 @@ void Document::invalidate_style_of_elements_affected_by_has() } } -void Document::invalidate_style_for_elements_affected_by_hover_change(Node& old_new_hovered_common_ancestor, GC::Ptr hovered_node) +void Document::invalidate_style_for_elements_affected_by_pseudo_class_change(CSS::PseudoClass pseudo_class, auto& element_slot, Node& old_new_common_ancestor, auto node) { - auto const& hover_rules = style_computer().get_hover_rules(); + auto const& rules = style_computer().get_pseudo_class_rule_cache(pseudo_class); - auto& root = old_new_hovered_common_ancestor.root(); + auto& root = old_new_common_ancestor.root(); auto shadow_root = is(root) ? static_cast(&root) : nullptr; auto& style_computer = this->style_computer(); @@ -1814,12 +1814,12 @@ void Document::invalidate_style_for_elements_affected_by_hover_change(Node& old_ return false; }; - auto matches_different_set_of_hover_rules_after_hovered_element_change = [&](Element const& element) { + auto matches_different_set_of_rules_after_state_change = [&](Element const& element) { bool result = false; - hover_rules.for_each_matching_rules(element, {}, [&](auto& rules) { + rules.for_each_matching_rules(element, {}, [&](auto& rules) { for (auto& rule : rules) { bool before = does_rule_match_on_element(element, rule); - TemporaryChange change { m_hovered_node, hovered_node }; + TemporaryChange change { element_slot, node }; bool after = does_rule_match_on_element(element, rule); if (before != after) { result = true; @@ -1831,17 +1831,17 @@ void Document::invalidate_style_for_elements_affected_by_hover_change(Node& old_ return result; }; - Function invalidate_hovered_elements_recursively = [&](Node& node) -> void { + Function invalidate_affected_elements_recursively = [&](Node& node) -> void { if (node.is_element()) { auto& element = static_cast(node); style_computer.push_ancestor(element); - if (element.affected_by_hover() && matches_different_set_of_hover_rules_after_hovered_element_change(element)) { + if (element.affected_by_pseudo_class(pseudo_class) && matches_different_set_of_rules_after_state_change(element)) { element.set_needs_style_update(true); } } node.for_each_child([&](auto& child) { - invalidate_hovered_elements_recursively(child); + invalidate_affected_elements_recursively(child); return IterationDecision::Continue; }); @@ -1849,12 +1849,12 @@ void Document::invalidate_style_for_elements_affected_by_hover_change(Node& old_ style_computer.pop_ancestor(static_cast(node)); }; - invalidate_hovered_elements_recursively(root); + invalidate_affected_elements_recursively(root); } -void Document::set_hovered_node(Node* node) +void Document::set_hovered_node(GC::Ptr node) { - if (m_hovered_node.ptr() == node) + if (m_hovered_node == node) return; GC::Ptr old_hovered_node = move(m_hovered_node); @@ -1868,11 +1868,11 @@ void Document::set_hovered_node(Node* node) new_hovered_node_root = node->root(); if (old_hovered_node_root != new_hovered_node_root) { if (old_hovered_node_root) - invalidate_style_for_elements_affected_by_hover_change(*old_hovered_node_root, node); + invalidate_style_for_elements_affected_by_pseudo_class_change(CSS::PseudoClass::Hover, m_hovered_node, *old_hovered_node_root, node); if (new_hovered_node_root) - invalidate_style_for_elements_affected_by_hover_change(*new_hovered_node_root, node); + invalidate_style_for_elements_affected_by_pseudo_class_change(CSS::PseudoClass::Hover, m_hovered_node, *new_hovered_node_root, node); } else { - invalidate_style_for_elements_affected_by_hover_change(*common_ancestor, node); + invalidate_style_for_elements_affected_by_pseudo_class_change(CSS::PseudoClass::Hover, m_hovered_node, *common_ancestor, node); } m_hovered_node = node; diff --git a/Libraries/LibWeb/DOM/Document.h b/Libraries/LibWeb/DOM/Document.h index 4a11faaef7a..e7d7174695a 100644 --- a/Libraries/LibWeb/DOM/Document.h +++ b/Libraries/LibWeb/DOM/Document.h @@ -253,8 +253,9 @@ public: virtual FlyString node_name() const override { return "#document"_fly_string; } - void invalidate_style_for_elements_affected_by_hover_change(Node& old_new_hovered_common_ancestor, GC::Ptr hovered_node); - void set_hovered_node(Node*); + void invalidate_style_for_elements_affected_by_pseudo_class_change(CSS::PseudoClass, auto& element_slot, Node& old_new_common_ancestor, auto node); + + void set_hovered_node(GC::Ptr); Node* hovered_node() { return m_hovered_node.ptr(); } Node const* hovered_node() const { return m_hovered_node.ptr(); } diff --git a/Libraries/LibWeb/DOM/Element.cpp b/Libraries/LibWeb/DOM/Element.cpp index e9a47a9093c..0781009eabb 100644 --- a/Libraries/LibWeb/DOM/Element.cpp +++ b/Libraries/LibWeb/DOM/Element.cpp @@ -1207,16 +1207,16 @@ GC::Ptr Element::get_pseudo_element_node(CSS::PseudoEleme return nullptr; } -bool Element::affected_by_hover() const +bool Element::affected_by_pseudo_class(CSS::PseudoClass pseudo_class) const { - if (m_computed_properties && m_computed_properties->did_match_any_hover_rules()) { + if (m_computed_properties && m_computed_properties->has_attempted_match_against_pseudo_class(pseudo_class)) { 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()) + if (pseudo_element.computed_properties->has_attempted_match_against_pseudo_class(pseudo_class)) return true; } } diff --git a/Libraries/LibWeb/DOM/Element.h b/Libraries/LibWeb/DOM/Element.h index 9a9871e2964..c1770af8b67 100644 --- a/Libraries/LibWeb/DOM/Element.h +++ b/Libraries/LibWeb/DOM/Element.h @@ -271,7 +271,7 @@ public: static GC::Ptr create_layout_node_for_display_type(DOM::Document&, CSS::Display const&, GC::Ref, Element*); - bool affected_by_hover() const; + [[nodiscard]] bool affected_by_pseudo_class(CSS::PseudoClass) const; bool includes_properties_from_invalidation_set(CSS::InvalidationSet const&) const; void set_pseudo_element_node(Badge, CSS::PseudoElement, GC::Ptr); diff --git a/Meta/Lagom/Tools/CodeGenerators/LibWeb/GenerateCSSPseudoClass.cpp b/Meta/Lagom/Tools/CodeGenerators/LibWeb/GenerateCSSPseudoClass.cpp index 09365ba7bca..8382e8ac26b 100644 --- a/Meta/Lagom/Tools/CodeGenerators/LibWeb/GenerateCSSPseudoClass.cpp +++ b/Meta/Lagom/Tools/CodeGenerators/LibWeb/GenerateCSSPseudoClass.cpp @@ -60,6 +60,7 @@ enum class PseudoClass { member_generator.appendln(" @name:titlecase@,"); }); generator.append(R"~~~( + __Count, }; Optional pseudo_class_from_string(StringView); @@ -123,6 +124,8 @@ Optional pseudo_class_from_string(StringView string) StringView pseudo_class_name(PseudoClass pseudo_class) { switch (pseudo_class) { + case PseudoClass::__Count: + VERIFY_NOT_REACHED(); )~~~"); pseudo_classes_data.for_each_member([&](auto& name, auto&) { @@ -144,6 +147,8 @@ StringView pseudo_class_name(PseudoClass pseudo_class) PseudoClassMetadata pseudo_class_metadata(PseudoClass pseudo_class) { switch (pseudo_class) { + case PseudoClass::__Count: + VERIFY_NOT_REACHED(); )~~~"); pseudo_classes_data.for_each_member([&](auto& name, JsonValue const& value) {