From e4245dc39e68d9376dfb2344c78119a938533319 Mon Sep 17 00:00:00 2001 From: Sam Atkins Date: Thu, 17 Oct 2024 13:48:00 +0100 Subject: [PATCH] LibWeb/CSS: Process style properties from CSSNestedDeclarations rules These are created when a style rule has properties listed after another rule. For example: ```css .test { --a: 1; --b: 1; --c: 1; .thing { /* ... */ } /* These are after a rule (.thing) so they're wrapped in a CSSNestedDeclarations: */ --d: 1; --e: 1; --f: 1; } ``` They're treated like a nested style rule with the exact same selectors as their containing style rule. --- Tests/LibWeb/Ref/css-nested-declarations.html | 12 ++++ .../css-nested-declarations-ref.html | 9 +++ .../Libraries/LibWeb/CSS/CSSStyleSheet.cpp | 6 +- Userland/Libraries/LibWeb/CSS/CSSStyleSheet.h | 2 +- .../Libraries/LibWeb/CSS/StyleComputer.cpp | 59 +++++++++++++++---- Userland/Libraries/LibWeb/CSS/StyleComputer.h | 7 ++- 6 files changed, 78 insertions(+), 17 deletions(-) create mode 100644 Tests/LibWeb/Ref/css-nested-declarations.html create mode 100644 Tests/LibWeb/Ref/reference/css-nested-declarations-ref.html diff --git a/Tests/LibWeb/Ref/css-nested-declarations.html b/Tests/LibWeb/Ref/css-nested-declarations.html new file mode 100644 index 000000000000..7b3034a51485 --- /dev/null +++ b/Tests/LibWeb/Ref/css-nested-declarations.html @@ -0,0 +1,12 @@ + + + +
Well hello friends!
diff --git a/Tests/LibWeb/Ref/reference/css-nested-declarations-ref.html b/Tests/LibWeb/Ref/reference/css-nested-declarations-ref.html new file mode 100644 index 000000000000..9f046d1c19e9 --- /dev/null +++ b/Tests/LibWeb/Ref/reference/css-nested-declarations-ref.html @@ -0,0 +1,9 @@ + + +
Well hello friends!
diff --git a/Userland/Libraries/LibWeb/CSS/CSSStyleSheet.cpp b/Userland/Libraries/LibWeb/CSS/CSSStyleSheet.cpp index 3881bd919140..04ad50f3c489 100644 --- a/Userland/Libraries/LibWeb/CSS/CSSStyleSheet.cpp +++ b/Userland/Libraries/LibWeb/CSS/CSSStyleSheet.cpp @@ -300,11 +300,11 @@ void CSSStyleSheet::for_each_effective_rule(TraversalOrder order, Functionfor_each_effective_rule(order, callback); } -void CSSStyleSheet::for_each_effective_style_rule(Function const& callback) const +void CSSStyleSheet::for_each_effective_style_producing_rule(Function const& callback) const { for_each_effective_rule(TraversalOrder::Preorder, [&](CSSRule const& rule) { - if (rule.type() == CSSRule::Type::Style) - callback(static_cast(rule)); + if (rule.type() == CSSRule::Type::Style || rule.type() == CSSRule::Type::NestedDeclarations) + callback(rule); }); } diff --git a/Userland/Libraries/LibWeb/CSS/CSSStyleSheet.h b/Userland/Libraries/LibWeb/CSS/CSSStyleSheet.h index a21ebbd49d9f..86f91a3b0f4a 100644 --- a/Userland/Libraries/LibWeb/CSS/CSSStyleSheet.h +++ b/Userland/Libraries/LibWeb/CSS/CSSStyleSheet.h @@ -57,7 +57,7 @@ class CSSStyleSheet final : public StyleSheet { WebIDL::ExceptionOr replace_sync(StringView text); void for_each_effective_rule(TraversalOrder, Function const& callback) const; - void for_each_effective_style_rule(Function const& callback) const; + void for_each_effective_style_producing_rule(Function const& callback) const; // Returns whether the match state of any media queries changed after evaluation. bool evaluate_media_queries(HTML::Window const&); void for_each_effective_keyframes_at_rule(Function const& callback) const; diff --git a/Userland/Libraries/LibWeb/CSS/StyleComputer.cpp b/Userland/Libraries/LibWeb/CSS/StyleComputer.cpp index b94d7e856911..705c81ae8612 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleComputer.cpp +++ b/Userland/Libraries/LibWeb/CSS/StyleComputer.cpp @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -96,6 +97,33 @@ struct Traits : public DefaultTraitstype() == CSSRule::Type::Style) + return static_cast(*rule).declaration(); + if (rule->type() == CSSRule::Type::NestedDeclarations) + return static_cast(*rule).declaration(); + VERIFY_NOT_REACHED(); +} + +SelectorList const& MatchingRule::absolutized_selectors() const +{ + if (rule->type() == CSSRule::Type::Style) + return static_cast(*rule).absolutized_selectors(); + if (rule->type() == CSSRule::Type::NestedDeclarations) + return static_cast(*rule->parent_rule()).absolutized_selectors(); + VERIFY_NOT_REACHED(); +} + +FlyString const& MatchingRule::qualified_layer_name() const +{ + if (rule->type() == CSSRule::Type::Style) + return static_cast(*rule).qualified_layer_name(); + if (rule->type() == CSSRule::Type::NestedDeclarations) + return static_cast(*rule->parent_rule()).qualified_layer_name(); + VERIFY_NOT_REACHED(); +} + static DOM::Element const* element_to_inherit_style_from(DOM::Element const*, Optional); StyleComputer::StyleComputer(DOM::Document& document) @@ -338,7 +366,7 @@ StyleComputer::RuleCache const& StyleComputer::rule_cache_for_cascade_origin(Cas [[nodiscard]] static bool filter_layer(FlyString const& qualified_layer_name, MatchingRule const& rule) { - if (rule.rule && rule.rule->qualified_layer_name() != qualified_layer_name) + if (rule.rule && rule.qualified_layer_name() != qualified_layer_name) return false; return true; } @@ -434,7 +462,7 @@ Vector StyleComputer::collect_matching_rules(DOM::Element const& e continue; } - auto const& selector = rule_to_run.rule->absolutized_selectors()[rule_to_run.selector_index]; + auto const& selector = rule_to_run.absolutized_selectors()[rule_to_run.selector_index]; if (should_reject_with_ancestor_filter(*selector)) { rule_to_run.skip = true; continue; @@ -461,7 +489,7 @@ Vector StyleComputer::collect_matching_rules(DOM::Element const& e if (element.is_shadow_host() && rule_root != element.shadow_root()) shadow_host_to_use = nullptr; - auto const& selector = rule_to_run.rule->absolutized_selectors()[rule_to_run.selector_index]; + auto const& selector = rule_to_run.absolutized_selectors()[rule_to_run.selector_index]; if (rule_to_run.can_use_fast_matches) { if (!SelectorEngine::fast_matches(selector, *rule_to_run.sheet, element, shadow_host_to_use)) @@ -478,8 +506,8 @@ Vector StyleComputer::collect_matching_rules(DOM::Element const& e static void sort_matching_rules(Vector& matching_rules) { quick_sort(matching_rules, [&](MatchingRule& a, MatchingRule& b) { - auto const& a_selector = a.rule->absolutized_selectors()[a.selector_index]; - auto const& b_selector = b.rule->absolutized_selectors()[b.selector_index]; + auto const& a_selector = a.absolutized_selectors()[a.selector_index]; + auto const& b_selector = b.absolutized_selectors()[b.selector_index]; auto a_specificity = a_selector->specificity(); auto b_specificity = b_selector->specificity(); if (a_specificity == b_specificity) { @@ -889,12 +917,12 @@ void StyleComputer::set_all_properties(DOM::Element& element, Optional pseudo_element, Vector const& matching_rules, CascadeOrigin cascade_origin, Important important, StyleProperties const& style_for_revert, StyleProperties const& style_for_revert_layer) const { for (auto const& match : matching_rules) { - for (auto const& property : match.rule->declaration().properties()) { + for (auto const& property : match.declaration().properties()) { if (important != property.important) continue; if (property.property_id == CSS::PropertyID::All) { - set_all_properties(element, pseudo_element, style, property.value, m_document, &match.rule->declaration(), style_for_revert, style_for_revert_layer, important); + set_all_properties(element, pseudo_element, style, property.value, m_document, &match.declaration(), style_for_revert, style_for_revert_layer, important); continue; } @@ -902,7 +930,7 @@ void StyleComputer::cascade_declarations(StyleProperties& style, DOM::Element& e if (property.value->is_unresolved()) property_value = Parser::Parser::resolve_unresolved_style_value(Parser::ParsingContext { document() }, element, pseudo_element, property.property_id, property.value->as_unresolved()); if (!property_value->is_unresolved()) - set_property_expanding_shorthands(style, property.property_id, property_value, &match.rule->declaration(), style_for_revert, style_for_revert_layer, important); + set_property_expanding_shorthands(style, property.property_id, property_value, &match.declaration(), style_for_revert, style_for_revert_layer, important); } } @@ -931,7 +959,7 @@ static void cascade_custom_properties(DOM::Element& element, Optionaldeclaration().custom_properties().size(); + needed_capacity += matching_rule.declaration().custom_properties().size(); if (!pseudo_element.has_value()) { if (auto const inline_style = element.inline_style()) @@ -941,7 +969,7 @@ static void cascade_custom_properties(DOM::Element& element, Optionaldeclaration().custom_properties()) { + for (auto const& it : matching_rule.declaration().custom_properties()) { auto style_value = it.value.value; if (style_value->is_revert_layer()) continue; @@ -2416,9 +2444,16 @@ NonnullOwnPtr StyleComputer::make_rule_cache_for_casca size_t style_sheet_index = 0; for_each_stylesheet(cascade_origin, [&](auto& sheet, JS::GCPtr shadow_root) { size_t rule_index = 0; - sheet.for_each_effective_style_rule([&](auto const& rule) { + sheet.for_each_effective_style_producing_rule([&](auto const& rule) { size_t selector_index = 0; - for (CSS::Selector const& selector : rule.absolutized_selectors()) { + SelectorList const& absolutized_selectors = [&]() { + if (rule.type() == CSSRule::Type::Style) + return static_cast(rule).absolutized_selectors(); + if (rule.type() == CSSRule::Type::NestedDeclarations) + return static_cast(*rule.parent_rule()).absolutized_selectors(); + VERIFY_NOT_REACHED(); + }(); + for (CSS::Selector const& selector : absolutized_selectors) { MatchingRule matching_rule { shadow_root, &rule, diff --git a/Userland/Libraries/LibWeb/CSS/StyleComputer.h b/Userland/Libraries/LibWeb/CSS/StyleComputer.h index fbb39a6aa8ee..80d53a5e0e47 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleComputer.h +++ b/Userland/Libraries/LibWeb/CSS/StyleComputer.h @@ -82,7 +82,7 @@ enum class CascadeOrigin : u8 { struct MatchingRule { JS::GCPtr shadow_root; - JS::GCPtr rule; + JS::GCPtr rule; // Either CSSStyleRule or CSSNestedDeclarations JS::GCPtr sheet; size_t style_sheet_index { 0 }; size_t rule_index { 0 }; @@ -94,6 +94,11 @@ struct MatchingRule { bool can_use_fast_matches { false }; bool must_be_hovered { false }; bool skip { false }; + + // Helpers to deal with the fact that `rule` might be a CSSStyleRule or a CSSNestedDeclarations + PropertyOwningCSSStyleDeclaration const& declaration() const; + SelectorList const& absolutized_selectors() const; + FlyString const& qualified_layer_name() const; }; struct FontFaceKey {