1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-26 02:47:34 +00:00

LibWeb: Make CSS rule cache smarter about pseudo elements

Instead of putting every rule that matches a pseudo element in the
same bucket, let them go in the best ID/class/tag name bucket instead.
Then, add a flag to MatchingRule that says whether it contains a
pseudo element in the rightmost compound selector.

When deciding which selectors to run for an element, we can now simply
filter in/out pseudo element selectors as appropriate depending on what
we're trying to match.

This fixes an issue where pages using Font Awesome had 1700+ rules in the
pseudo-element rule cache. (This meant all those rules had to run
against every element twice or more while instantiating pseudo elements.)
This commit is contained in:
Andreas Kling 2023-03-09 19:27:23 +01:00
parent 2a607e9ebc
commit 4bfdc4db17
2 changed files with 49 additions and 39 deletions

View file

@ -163,22 +163,28 @@ Vector<MatchingRule> StyleComputer::collect_matching_rules(DOM::Element const& e
auto const& rule_cache = rule_cache_for_cascade_origin(cascade_origin); auto const& rule_cache = rule_cache_for_cascade_origin(cascade_origin);
Vector<MatchingRule> rules_to_run; Vector<MatchingRule> rules_to_run;
if (pseudo_element.has_value()) { auto add_rules_to_run = [&](Vector<MatchingRule> const& rules) {
if (auto it = rule_cache.rules_by_pseudo_element.find(pseudo_element.value()); it != rule_cache.rules_by_pseudo_element.end()) if (pseudo_element.has_value()) {
rules_to_run.extend(it->value); for (auto& rule : rules) {
} else { if (rule.contains_pseudo_element)
for (auto const& class_name : element.class_names()) { rules_to_run.append(rule);
if (auto it = rule_cache.rules_by_class.find(FlyString::from_utf8(class_name).release_value_but_fixme_should_propagate_errors()); it != rule_cache.rules_by_class.end()) }
rules_to_run.extend(it->value); } else {
rules_to_run.extend(rules);
} }
if (auto id = element.get_attribute(HTML::AttributeNames::id); !id.is_null()) { };
if (auto it = rule_cache.rules_by_id.find(FlyString::from_utf8(id).release_value_but_fixme_should_propagate_errors()); it != rule_cache.rules_by_id.end())
rules_to_run.extend(it->value); for (auto const& class_name : element.class_names()) {
} if (auto it = rule_cache.rules_by_class.find(FlyString::from_utf8(class_name).release_value_but_fixme_should_propagate_errors()); it != rule_cache.rules_by_class.end())
if (auto it = rule_cache.rules_by_tag_name.find(FlyString::from_utf8(element.local_name()).release_value_but_fixme_should_propagate_errors()); it != rule_cache.rules_by_tag_name.end()) add_rules_to_run(it->value);
rules_to_run.extend(it->value);
rules_to_run.extend(rule_cache.other_rules);
} }
if (auto id = element.get_attribute(HTML::AttributeNames::id); !id.is_null()) {
if (auto it = rule_cache.rules_by_id.find(FlyString::from_utf8(id).release_value_but_fixme_should_propagate_errors()); it != rule_cache.rules_by_id.end())
add_rules_to_run(it->value);
}
if (auto it = rule_cache.rules_by_tag_name.find(FlyString::from_utf8(element.local_name()).release_value_but_fixme_should_propagate_errors()); it != rule_cache.rules_by_tag_name.end())
add_rules_to_run(it->value);
add_rules_to_run(rule_cache.other_rules);
Vector<MatchingRule> matching_rules; Vector<MatchingRule> matching_rules;
matching_rules.ensure_capacity(rules_to_run.size()); matching_rules.ensure_capacity(rules_to_run.size());
@ -1443,37 +1449,41 @@ NonnullOwnPtr<StyleComputer::RuleCache> StyleComputer::make_rule_cache_for_casca
sheet.for_each_effective_style_rule([&](auto const& rule) { sheet.for_each_effective_style_rule([&](auto const& rule) {
size_t selector_index = 0; size_t selector_index = 0;
for (CSS::Selector const& selector : rule.selectors()) { for (CSS::Selector const& selector : rule.selectors()) {
MatchingRule matching_rule { &rule, style_sheet_index, rule_index, selector_index, selector.specificity() }; MatchingRule matching_rule {
&rule,
style_sheet_index,
rule_index,
selector_index,
selector.specificity(),
};
bool added_to_bucket = false;
for (auto const& simple_selector : selector.compound_selectors().last().simple_selectors) { for (auto const& simple_selector : selector.compound_selectors().last().simple_selectors) {
if (simple_selector.type == CSS::Selector::SimpleSelector::Type::PseudoElement) { if (simple_selector.type == CSS::Selector::SimpleSelector::Type::PseudoElement) {
rule_cache->rules_by_pseudo_element.ensure(simple_selector.pseudo_element()).append(move(matching_rule)); matching_rule.contains_pseudo_element = true;
++num_pseudo_element_rules; ++num_pseudo_element_rules;
added_to_bucket = true;
break; break;
} }
} }
if (!added_to_bucket) {
for (auto const& simple_selector : selector.compound_selectors().last().simple_selectors) { bool added_to_bucket = false;
if (simple_selector.type == CSS::Selector::SimpleSelector::Type::Id) { for (auto const& simple_selector : selector.compound_selectors().last().simple_selectors) {
rule_cache->rules_by_id.ensure(simple_selector.name()).append(move(matching_rule)); if (simple_selector.type == CSS::Selector::SimpleSelector::Type::Id) {
++num_id_rules; rule_cache->rules_by_id.ensure(simple_selector.name()).append(move(matching_rule));
added_to_bucket = true; ++num_id_rules;
break; added_to_bucket = true;
} break;
if (simple_selector.type == CSS::Selector::SimpleSelector::Type::Class) { }
rule_cache->rules_by_class.ensure(simple_selector.name()).append(move(matching_rule)); if (simple_selector.type == CSS::Selector::SimpleSelector::Type::Class) {
++num_class_rules; rule_cache->rules_by_class.ensure(simple_selector.name()).append(move(matching_rule));
added_to_bucket = true; ++num_class_rules;
break; added_to_bucket = true;
} break;
if (simple_selector.type == CSS::Selector::SimpleSelector::Type::TagName) { }
rule_cache->rules_by_tag_name.ensure(simple_selector.name()).append(move(matching_rule)); if (simple_selector.type == CSS::Selector::SimpleSelector::Type::TagName) {
++num_tag_name_rules; rule_cache->rules_by_tag_name.ensure(simple_selector.name()).append(move(matching_rule));
added_to_bucket = true; ++num_tag_name_rules;
break; added_to_bucket = true;
} break;
} }
} }
if (!added_to_bucket) if (!added_to_bucket)

View file

@ -26,6 +26,7 @@ struct MatchingRule {
size_t rule_index { 0 }; size_t rule_index { 0 };
size_t selector_index { 0 }; size_t selector_index { 0 };
u32 specificity { 0 }; u32 specificity { 0 };
bool contains_pseudo_element { false };
}; };
class PropertyDependencyNode : public RefCounted<PropertyDependencyNode> { class PropertyDependencyNode : public RefCounted<PropertyDependencyNode> {
@ -111,7 +112,6 @@ private:
HashMap<FlyString, Vector<MatchingRule>> rules_by_id; HashMap<FlyString, Vector<MatchingRule>> rules_by_id;
HashMap<FlyString, Vector<MatchingRule>> rules_by_class; HashMap<FlyString, Vector<MatchingRule>> rules_by_class;
HashMap<FlyString, Vector<MatchingRule>> rules_by_tag_name; HashMap<FlyString, Vector<MatchingRule>> rules_by_tag_name;
HashMap<Selector::PseudoElement, Vector<MatchingRule>> rules_by_pseudo_element;
Vector<MatchingRule> other_rules; Vector<MatchingRule> other_rules;
}; };