mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-22 15:40:19 +00:00
LibWeb: Implement the CSS rule cache optimization for UA style as well
We were already sorting the author style selectors into buckets. Now we do it for the built-in UA style as well. This means less work for the selector engine everywhere :^)
This commit is contained in:
parent
2042993997
commit
72569bca1c
Notes:
sideshowbarker
2024-07-17 01:04:03 +09:00
Author: https://github.com/awesomekling Commit: https://github.com/SerenityOS/serenity/commit/72569bca1c Pull-request: https://github.com/SerenityOS/serenity/pull/17763 Reviewed-by: https://github.com/ADKaster Reviewed-by: https://github.com/Hendiadyoin1 Reviewed-by: https://github.com/nico
2 changed files with 64 additions and 57 deletions
|
@ -146,55 +146,47 @@ void StyleComputer::for_each_stylesheet(CascadeOrigin cascade_origin, Callback c
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
StyleComputer::RuleCache const& StyleComputer::rule_cache_for_cascade_origin(CascadeOrigin cascade_origin) const
|
||||||
|
{
|
||||||
|
switch (cascade_origin) {
|
||||||
|
case CascadeOrigin::Author:
|
||||||
|
return *m_author_rule_cache;
|
||||||
|
case CascadeOrigin::UserAgent:
|
||||||
|
return *m_user_agent_rule_cache;
|
||||||
|
default:
|
||||||
|
TODO();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Vector<MatchingRule> StyleComputer::collect_matching_rules(DOM::Element const& element, CascadeOrigin cascade_origin, Optional<CSS::Selector::PseudoElement> pseudo_element) const
|
Vector<MatchingRule> StyleComputer::collect_matching_rules(DOM::Element const& element, CascadeOrigin cascade_origin, Optional<CSS::Selector::PseudoElement> pseudo_element) const
|
||||||
{
|
{
|
||||||
if (cascade_origin == CascadeOrigin::Author) {
|
auto const& rule_cache = rule_cache_for_cascade_origin(cascade_origin);
|
||||||
Vector<MatchingRule> rules_to_run;
|
|
||||||
if (pseudo_element.has_value()) {
|
|
||||||
if (auto it = m_rule_cache->rules_by_pseudo_element.find(pseudo_element.value()); it != m_rule_cache->rules_by_pseudo_element.end())
|
|
||||||
rules_to_run.extend(it->value);
|
|
||||||
} else {
|
|
||||||
for (auto const& class_name : element.class_names()) {
|
|
||||||
if (auto it = m_rule_cache->rules_by_class.find(FlyString::from_utf8(class_name).release_value_but_fixme_should_propagate_errors()); it != m_rule_cache->rules_by_class.end())
|
|
||||||
rules_to_run.extend(it->value);
|
|
||||||
}
|
|
||||||
if (auto id = element.get_attribute(HTML::AttributeNames::id); !id.is_null()) {
|
|
||||||
if (auto it = m_rule_cache->rules_by_id.find(FlyString::from_utf8(id).release_value_but_fixme_should_propagate_errors()); it != m_rule_cache->rules_by_id.end())
|
|
||||||
rules_to_run.extend(it->value);
|
|
||||||
}
|
|
||||||
if (auto it = m_rule_cache->rules_by_tag_name.find(FlyString::from_utf8(element.local_name()).release_value_but_fixme_should_propagate_errors()); it != m_rule_cache->rules_by_tag_name.end())
|
|
||||||
rules_to_run.extend(it->value);
|
|
||||||
rules_to_run.extend(m_rule_cache->other_rules);
|
|
||||||
}
|
|
||||||
|
|
||||||
Vector<MatchingRule> matching_rules;
|
Vector<MatchingRule> rules_to_run;
|
||||||
matching_rules.ensure_capacity(rules_to_run.size());
|
if (pseudo_element.has_value()) {
|
||||||
for (auto const& rule_to_run : rules_to_run) {
|
if (auto it = rule_cache.rules_by_pseudo_element.find(pseudo_element.value()); it != rule_cache.rules_by_pseudo_element.end())
|
||||||
auto const& selector = rule_to_run.rule->selectors()[rule_to_run.selector_index];
|
rules_to_run.extend(it->value);
|
||||||
if (SelectorEngine::matches(selector, element, pseudo_element))
|
} else {
|
||||||
matching_rules.append(rule_to_run);
|
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())
|
||||||
|
rules_to_run.extend(it->value);
|
||||||
}
|
}
|
||||||
return matching_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);
|
||||||
|
}
|
||||||
|
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())
|
||||||
|
rules_to_run.extend(it->value);
|
||||||
|
rules_to_run.extend(rule_cache.other_rules);
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector<MatchingRule> matching_rules;
|
Vector<MatchingRule> matching_rules;
|
||||||
size_t style_sheet_index = 0;
|
matching_rules.ensure_capacity(rules_to_run.size());
|
||||||
for_each_stylesheet(cascade_origin, [&](auto& sheet) {
|
for (auto const& rule_to_run : rules_to_run) {
|
||||||
size_t rule_index = 0;
|
auto const& selector = rule_to_run.rule->selectors()[rule_to_run.selector_index];
|
||||||
sheet.for_each_effective_style_rule([&](auto const& rule) {
|
if (SelectorEngine::matches(selector, element, pseudo_element))
|
||||||
size_t selector_index = 0;
|
matching_rules.append(rule_to_run);
|
||||||
for (auto& selector : rule.selectors()) {
|
}
|
||||||
if (SelectorEngine::matches(*selector, element, pseudo_element)) {
|
|
||||||
matching_rules.append({ &rule, style_sheet_index, rule_index, selector_index, selector->specificity() });
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
++selector_index;
|
|
||||||
}
|
|
||||||
++rule_index;
|
|
||||||
});
|
|
||||||
++style_sheet_index;
|
|
||||||
});
|
|
||||||
|
|
||||||
return matching_rules;
|
return matching_rules;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1430,16 +1422,14 @@ bool PropertyDependencyNode::has_cycles()
|
||||||
|
|
||||||
void StyleComputer::build_rule_cache_if_needed() const
|
void StyleComputer::build_rule_cache_if_needed() const
|
||||||
{
|
{
|
||||||
if (m_rule_cache)
|
if (m_author_rule_cache && m_user_agent_rule_cache)
|
||||||
return;
|
return;
|
||||||
const_cast<StyleComputer&>(*this).build_rule_cache();
|
const_cast<StyleComputer&>(*this).build_rule_cache();
|
||||||
}
|
}
|
||||||
|
|
||||||
void StyleComputer::build_rule_cache()
|
NonnullOwnPtr<StyleComputer::RuleCache> StyleComputer::make_rule_cache_for_cascade_origin(CascadeOrigin cascade_origin)
|
||||||
{
|
{
|
||||||
// FIXME: Make a rule cache for UA style as well.
|
auto rule_cache = make<RuleCache>();
|
||||||
|
|
||||||
m_rule_cache = make<RuleCache>();
|
|
||||||
|
|
||||||
size_t num_class_rules = 0;
|
size_t num_class_rules = 0;
|
||||||
size_t num_id_rules = 0;
|
size_t num_id_rules = 0;
|
||||||
|
@ -1448,7 +1438,7 @@ void StyleComputer::build_rule_cache()
|
||||||
|
|
||||||
Vector<MatchingRule> matching_rules;
|
Vector<MatchingRule> matching_rules;
|
||||||
size_t style_sheet_index = 0;
|
size_t style_sheet_index = 0;
|
||||||
for_each_stylesheet(CascadeOrigin::Author, [&](auto& sheet) {
|
for_each_stylesheet(cascade_origin, [&](auto& sheet) {
|
||||||
size_t rule_index = 0;
|
size_t rule_index = 0;
|
||||||
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;
|
||||||
|
@ -1458,7 +1448,7 @@ void StyleComputer::build_rule_cache()
|
||||||
bool added_to_bucket = false;
|
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) {
|
||||||
m_rule_cache->rules_by_pseudo_element.ensure(simple_selector.pseudo_element()).append(move(matching_rule));
|
rule_cache->rules_by_pseudo_element.ensure(simple_selector.pseudo_element()).append(move(matching_rule));
|
||||||
++num_pseudo_element_rules;
|
++num_pseudo_element_rules;
|
||||||
added_to_bucket = true;
|
added_to_bucket = true;
|
||||||
break;
|
break;
|
||||||
|
@ -1467,19 +1457,19 @@ void StyleComputer::build_rule_cache()
|
||||||
if (!added_to_bucket) {
|
if (!added_to_bucket) {
|
||||||
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::Id) {
|
if (simple_selector.type == CSS::Selector::SimpleSelector::Type::Id) {
|
||||||
m_rule_cache->rules_by_id.ensure(simple_selector.name()).append(move(matching_rule));
|
rule_cache->rules_by_id.ensure(simple_selector.name()).append(move(matching_rule));
|
||||||
++num_id_rules;
|
++num_id_rules;
|
||||||
added_to_bucket = true;
|
added_to_bucket = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (simple_selector.type == CSS::Selector::SimpleSelector::Type::Class) {
|
if (simple_selector.type == CSS::Selector::SimpleSelector::Type::Class) {
|
||||||
m_rule_cache->rules_by_class.ensure(simple_selector.name()).append(move(matching_rule));
|
rule_cache->rules_by_class.ensure(simple_selector.name()).append(move(matching_rule));
|
||||||
++num_class_rules;
|
++num_class_rules;
|
||||||
added_to_bucket = true;
|
added_to_bucket = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (simple_selector.type == CSS::Selector::SimpleSelector::Type::TagName) {
|
if (simple_selector.type == CSS::Selector::SimpleSelector::Type::TagName) {
|
||||||
m_rule_cache->rules_by_tag_name.ensure(simple_selector.name()).append(move(matching_rule));
|
rule_cache->rules_by_tag_name.ensure(simple_selector.name()).append(move(matching_rule));
|
||||||
++num_tag_name_rules;
|
++num_tag_name_rules;
|
||||||
added_to_bucket = true;
|
added_to_bucket = true;
|
||||||
break;
|
break;
|
||||||
|
@ -1487,7 +1477,7 @@ void StyleComputer::build_rule_cache()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!added_to_bucket)
|
if (!added_to_bucket)
|
||||||
m_rule_cache->other_rules.append(move(matching_rule));
|
rule_cache->other_rules.append(move(matching_rule));
|
||||||
|
|
||||||
++selector_index;
|
++selector_index;
|
||||||
}
|
}
|
||||||
|
@ -1502,14 +1492,25 @@ void StyleComputer::build_rule_cache()
|
||||||
dbgln(" Class: {}", num_class_rules);
|
dbgln(" Class: {}", num_class_rules);
|
||||||
dbgln(" TagName: {}", num_tag_name_rules);
|
dbgln(" TagName: {}", num_tag_name_rules);
|
||||||
dbgln("PseudoElement: {}", num_pseudo_element_rules);
|
dbgln("PseudoElement: {}", num_pseudo_element_rules);
|
||||||
dbgln(" Other: {}", m_rule_cache->other_rules.size());
|
dbgln(" Other: {}", rule_cache->other_rules.size());
|
||||||
dbgln(" Total: {}", num_class_rules + num_id_rules + num_tag_name_rules + m_rule_cache->other_rules.size());
|
dbgln(" Total: {}", num_class_rules + num_id_rules + num_tag_name_rules + rule_cache->other_rules.size());
|
||||||
}
|
}
|
||||||
|
return rule_cache;
|
||||||
|
}
|
||||||
|
|
||||||
|
void StyleComputer::build_rule_cache()
|
||||||
|
{
|
||||||
|
m_author_rule_cache = make_rule_cache_for_cascade_origin(CascadeOrigin::Author);
|
||||||
|
m_user_agent_rule_cache = make_rule_cache_for_cascade_origin(CascadeOrigin::UserAgent);
|
||||||
}
|
}
|
||||||
|
|
||||||
void StyleComputer::invalidate_rule_cache()
|
void StyleComputer::invalidate_rule_cache()
|
||||||
{
|
{
|
||||||
m_rule_cache = nullptr;
|
m_author_rule_cache = nullptr;
|
||||||
|
|
||||||
|
// NOTE: It might not be necessary to throw away the UA rule cache.
|
||||||
|
// If we are sure that it's safe, we could keep it as an optimization.
|
||||||
|
m_user_agent_rule_cache = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
CSSPixelRect StyleComputer::viewport_rect() const
|
CSSPixelRect StyleComputer::viewport_rect() const
|
||||||
|
|
|
@ -114,7 +114,13 @@ private:
|
||||||
HashMap<Selector::PseudoElement, Vector<MatchingRule>> rules_by_pseudo_element;
|
HashMap<Selector::PseudoElement, Vector<MatchingRule>> rules_by_pseudo_element;
|
||||||
Vector<MatchingRule> other_rules;
|
Vector<MatchingRule> other_rules;
|
||||||
};
|
};
|
||||||
OwnPtr<RuleCache> m_rule_cache;
|
|
||||||
|
NonnullOwnPtr<RuleCache> make_rule_cache_for_cascade_origin(CascadeOrigin);
|
||||||
|
|
||||||
|
RuleCache const& rule_cache_for_cascade_origin(CascadeOrigin) const;
|
||||||
|
|
||||||
|
OwnPtr<RuleCache> m_author_rule_cache;
|
||||||
|
OwnPtr<RuleCache> m_user_agent_rule_cache;
|
||||||
|
|
||||||
class FontLoader;
|
class FontLoader;
|
||||||
HashMap<String, NonnullOwnPtr<FontLoader>> m_loaded_fonts;
|
HashMap<String, NonnullOwnPtr<FontLoader>> m_loaded_fonts;
|
||||||
|
|
Loading…
Reference in a new issue