From 3e970540b4a1663f3569ebe049b71c60100ee018 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Tue, 14 Mar 2023 16:36:20 +0100 Subject: [PATCH] LibWeb: Don't compute full style for ::before/::after unless matched Before this patch, we would build full computed style for these pseudo elements, for every DOM element, even if no ::before/::after selector actually matched. This was a colossal waste of time, and we can also just not do that. Instead, just abort pseudo element style resolution early if no relevant selectors matched. :^) --- .../Libraries/LibWeb/CSS/StyleComputer.cpp | 24 +++++++++++++++++-- Userland/Libraries/LibWeb/CSS/StyleComputer.h | 10 +++++++- .../Libraries/LibWeb/Layout/TreeBuilder.cpp | 7 ++++-- 3 files changed, 36 insertions(+), 5 deletions(-) diff --git a/Userland/Libraries/LibWeb/CSS/StyleComputer.cpp b/Userland/Libraries/LibWeb/CSS/StyleComputer.cpp index 183f77a162..921021dd95 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleComputer.cpp +++ b/Userland/Libraries/LibWeb/CSS/StyleComputer.cpp @@ -867,7 +867,7 @@ static ErrorOr cascade_custom_properties(DOM::Element& element, Vector StyleComputer::compute_cascaded_values(StyleProperties& style, DOM::Element& element, Optional pseudo_element) const +ErrorOr StyleComputer::compute_cascaded_values(StyleProperties& style, DOM::Element& element, Optional pseudo_element, bool& did_match_any_pseudo_element_rules) const { // First, we collect all the CSS rules whose selectors match `element`: MatchingRuleSet matching_rule_set; @@ -876,6 +876,11 @@ ErrorOr StyleComputer::compute_cascaded_values(StyleProperties& style, DOM matching_rule_set.author_rules = collect_matching_rules(element, CascadeOrigin::Author, pseudo_element); sort_matching_rules(matching_rule_set.author_rules); + if (pseudo_element.has_value() && matching_rule_set.author_rules.is_empty() && matching_rule_set.user_agent_rules.is_empty()) { + did_match_any_pseudo_element_rules = false; + return {}; + } + // Then we resolve all the CSS custom properties ("variables") for this element: // FIXME: Look into how custom properties should interact with pseudo elements and support that properly. if (!pseudo_element.has_value()) @@ -1400,12 +1405,27 @@ NonnullRefPtr StyleComputer::create_document_style() const } ErrorOr> StyleComputer::compute_style(DOM::Element& element, Optional pseudo_element) const +{ + auto style = TRY(compute_style_impl(element, move(pseudo_element), ComputeStyleMode::Normal)); + return style.release_nonnull(); +} + +ErrorOr> StyleComputer::compute_pseudo_element_style_if_needed(DOM::Element& element, Optional pseudo_element) const +{ + return compute_style_impl(element, move(pseudo_element), ComputeStyleMode::CreatePseudoElementStyleIfNeeded); +} + +ErrorOr> StyleComputer::compute_style_impl(DOM::Element& element, Optional pseudo_element, ComputeStyleMode mode) const { build_rule_cache_if_needed(); auto style = StyleProperties::create(); // 1. Perform the cascade. This produces the "specified style" - TRY(compute_cascaded_values(style, element, pseudo_element)); + bool did_match_any_pseudo_element_rules = false; + TRY(compute_cascaded_values(style, element, pseudo_element, did_match_any_pseudo_element_rules)); + + if (mode == ComputeStyleMode::CreatePseudoElementStyleIfNeeded && !did_match_any_pseudo_element_rules) + return nullptr; // 2. Compute the font, since that may be needed for font-relative CSS units compute_font(style, &element, pseudo_element); diff --git a/Userland/Libraries/LibWeb/CSS/StyleComputer.h b/Userland/Libraries/LibWeb/CSS/StyleComputer.h index ef1f2a1904..8824657b41 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleComputer.h +++ b/Userland/Libraries/LibWeb/CSS/StyleComputer.h @@ -56,7 +56,9 @@ public: DOM::Document const& document() const { return m_document; } NonnullRefPtr create_document_style() const; + ErrorOr> compute_style(DOM::Element&, Optional = {}) const; + ErrorOr> compute_pseudo_element_style_if_needed(DOM::Element&, Optional) const; // https://www.w3.org/TR/css-cascade/#origin enum class CascadeOrigin { @@ -78,7 +80,13 @@ public: void load_fonts_from_sheet(CSSStyleSheet const&); private: - ErrorOr compute_cascaded_values(StyleProperties&, DOM::Element&, Optional) const; + enum class ComputeStyleMode { + Normal, + CreatePseudoElementStyleIfNeeded, + }; + + ErrorOr> compute_style_impl(DOM::Element&, Optional, ComputeStyleMode) const; + ErrorOr compute_cascaded_values(StyleProperties&, DOM::Element&, Optional, bool& did_match_any_pseudo_element_rules) const; void compute_font(StyleProperties&, DOM::Element const*, Optional) const; void compute_defaulted_values(StyleProperties&, DOM::Element const*, Optional) const; void absolutize_values(StyleProperties&, DOM::Element const*, Optional) const; diff --git a/Userland/Libraries/LibWeb/Layout/TreeBuilder.cpp b/Userland/Libraries/LibWeb/Layout/TreeBuilder.cpp index 47210d3c31..cde362e5f8 100644 --- a/Userland/Libraries/LibWeb/Layout/TreeBuilder.cpp +++ b/Userland/Libraries/LibWeb/Layout/TreeBuilder.cpp @@ -154,7 +154,10 @@ ErrorOr TreeBuilder::create_pseudo_element_if_needed(DOM::Element& element auto& document = element.document(); auto& style_computer = document.style_computer(); - auto pseudo_element_style = TRY(style_computer.compute_style(element, pseudo_element)); + auto pseudo_element_style = TRY(style_computer.compute_pseudo_element_style_if_needed(element, pseudo_element)); + if (!pseudo_element_style) + return {}; + auto pseudo_element_content = pseudo_element_style->content(); auto pseudo_element_display = pseudo_element_style->display(); // ::before and ::after only exist if they have content. `content: normal` computes to `none` for them. @@ -164,7 +167,7 @@ ErrorOr TreeBuilder::create_pseudo_element_if_needed(DOM::Element& element || pseudo_element_content.type == CSS::ContentData::Type::None) return {}; - auto pseudo_element_node = DOM::Element::create_layout_node_for_display_type(document, pseudo_element_display, pseudo_element_style, nullptr); + auto pseudo_element_node = DOM::Element::create_layout_node_for_display_type(document, pseudo_element_display, *pseudo_element_style, nullptr); if (!pseudo_element_node) return {};