diff --git a/Tests/LibWeb/Ref/css-nested-declarations.html b/Tests/LibWeb/Ref/css-nested-declarations.html
new file mode 100644
index 0000000000..7b3034a514
--- /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 0000000000..9f046d1c19
--- /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 3881bd9191..04ad50f3c4 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 a21ebbd49d..86f91a3b0f 100644
--- a/Userland/Libraries/LibWeb/CSS/CSSStyleSheet.h
+++ b/Userland/Libraries/LibWeb/CSS/CSSStyleSheet.h
@@ -57,7 +57,7 @@ public:
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 b94d7e8569..705c81ae86 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 fbb39a6aa8..80d53a5e0e 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 {