From bbf67faa9586b21ca7b4feaae33fb54374f3a15c Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Wed, 13 Mar 2024 11:47:25 +0100 Subject: [PATCH] LibWeb: Add CSS rule buckets for pseudo elements, and for :root If a selector must match a pseudo element, or must match the root element, we now cache that information in the MatchingRule struct. We also introduce separate buckets for these rules, so we can avoid running them altogether if the current element can't possibly match. This cuts the number of selectors evaluated by 32% when loading our GitHub repo page https://github.com/SerenityOS/serenity --- .../Libraries/LibWeb/CSS/StyleComputer.cpp | 32 +++++++++++++++---- Userland/Libraries/LibWeb/CSS/StyleComputer.h | 4 +++ 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/Userland/Libraries/LibWeb/CSS/StyleComputer.cpp b/Userland/Libraries/LibWeb/CSS/StyleComputer.cpp index 1b7ab8424d8ac7..a59c636ff028f9 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleComputer.cpp +++ b/Userland/Libraries/LibWeb/CSS/StyleComputer.cpp @@ -336,6 +336,10 @@ Vector StyleComputer::collect_matching_rules(DOM::Element const& e } if (auto it = rule_cache.rules_by_tag_name.find(element.local_name()); it != rule_cache.rules_by_tag_name.end()) add_rules_to_run(it->value); + if (pseudo_element.has_value()) + add_rules_to_run(rule_cache.pseudo_element_rules); + if (element.is_document_element()) + add_rules_to_run(rule_cache.root_rules); add_rules_to_run(rule_cache.other_rules); Vector matching_rules; @@ -2290,6 +2294,7 @@ NonnullOwnPtr StyleComputer::make_rule_cache_for_casca size_t num_id_rules = 0; size_t num_tag_name_rules = 0; size_t num_pseudo_element_rules = 0; + size_t num_root_rules = 0; Vector matching_rules; size_t style_sheet_index = 0; @@ -2310,10 +2315,18 @@ NonnullOwnPtr StyleComputer::make_rule_cache_for_casca }; for (auto const& simple_selector : selector.compound_selectors().last().simple_selectors) { - if (simple_selector.type == CSS::Selector::SimpleSelector::Type::PseudoElement) { - matching_rule.contains_pseudo_element = true; - ++num_pseudo_element_rules; - break; + if (!matching_rule.contains_pseudo_element) { + if (simple_selector.type == CSS::Selector::SimpleSelector::Type::PseudoElement) { + matching_rule.contains_pseudo_element = true; + ++num_pseudo_element_rules; + } + } + if (!matching_rule.contains_root_pseudo_class) { + if (simple_selector.type == CSS::Selector::SimpleSelector::Type::PseudoClass + && simple_selector.pseudo_class().type == CSS::PseudoClass::Root) { + matching_rule.contains_root_pseudo_class = true; + ++num_root_rules; + } } } @@ -2338,8 +2351,14 @@ NonnullOwnPtr StyleComputer::make_rule_cache_for_casca break; } } - if (!added_to_bucket) - rule_cache->other_rules.append(move(matching_rule)); + if (!added_to_bucket) { + if (matching_rule.contains_pseudo_element) + rule_cache->pseudo_element_rules.append(move(matching_rule)); + else if (matching_rule.contains_root_pseudo_class) + rule_cache->root_rules.append(move(matching_rule)); + else + rule_cache->other_rules.append(move(matching_rule)); + } ++selector_index; } @@ -2391,6 +2410,7 @@ NonnullOwnPtr StyleComputer::make_rule_cache_for_casca dbgln(" Class: {}", num_class_rules); dbgln(" TagName: {}", num_tag_name_rules); dbgln("PseudoElement: {}", num_pseudo_element_rules); + dbgln(" Root: {}", num_root_rules); dbgln(" Other: {}", rule_cache->other_rules.size()); dbgln(" Total: {}", num_class_rules + num_id_rules + num_tag_name_rules + rule_cache->other_rules.size()); } diff --git a/Userland/Libraries/LibWeb/CSS/StyleComputer.h b/Userland/Libraries/LibWeb/CSS/StyleComputer.h index bd15fec532a3f5..cf5d60ffdd3278 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleComputer.h +++ b/Userland/Libraries/LibWeb/CSS/StyleComputer.h @@ -37,8 +37,10 @@ struct MatchingRule { size_t style_sheet_index { 0 }; size_t rule_index { 0 }; size_t selector_index { 0 }; + u32 specificity { 0 }; bool contains_pseudo_element { false }; + bool contains_root_pseudo_class { false }; }; struct FontFaceKey { @@ -129,6 +131,8 @@ class StyleComputer { HashMap> rules_by_id; HashMap> rules_by_class; HashMap> rules_by_tag_name; + Vector pseudo_element_rules; + Vector root_rules; Vector other_rules; HashMap> rules_by_animation_keyframes;