Skip to content

Commit

Permalink
LibWeb/CSS: Process style properties from CSSNestedDeclarations rules
Browse files Browse the repository at this point in the history
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.

(cherry picked from commit e4245dc39e68d9376dfb2344c78119a938533319)
  • Loading branch information
AtkinsSJ authored and nico committed Nov 18, 2024
1 parent e93ebf4 commit f616bda
Show file tree
Hide file tree
Showing 6 changed files with 78 additions and 17 deletions.
12 changes: 12 additions & 0 deletions Tests/LibWeb/Ref/css-nested-declarations.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<!DOCTYPE html>
<link rel="match" href="reference/css-nested-declarations-ref.html" />
<style>
#target {
background-color: green;
@media all {}
border: 1px solid blue;
.whatever {}
font-size: 60px;
}
</style>
<div id="target">Well hello friends!</div>
9 changes: 9 additions & 0 deletions Tests/LibWeb/Ref/reference/css-nested-declarations-ref.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<!DOCTYPE html>
<style>
#target {
background-color: green;
border: 1px solid blue;
font-size: 60px;
}
</style>
<div id="target">Well hello friends!</div>
6 changes: 3 additions & 3 deletions Userland/Libraries/LibWeb/CSS/CSSStyleSheet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -300,11 +300,11 @@ void CSSStyleSheet::for_each_effective_rule(TraversalOrder order, Function<void(
m_rules->for_each_effective_rule(order, callback);
}

void CSSStyleSheet::for_each_effective_style_rule(Function<void(CSSStyleRule const&)> const& callback) const
void CSSStyleSheet::for_each_effective_style_producing_rule(Function<void(CSSRule const&)> const& callback) const
{
for_each_effective_rule(TraversalOrder::Preorder, [&](CSSRule const& rule) {
if (rule.type() == CSSRule::Type::Style)
callback(static_cast<CSSStyleRule const&>(rule));
if (rule.type() == CSSRule::Type::Style || rule.type() == CSSRule::Type::NestedDeclarations)
callback(rule);
});
}

Expand Down
2 changes: 1 addition & 1 deletion Userland/Libraries/LibWeb/CSS/CSSStyleSheet.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ class CSSStyleSheet final : public StyleSheet {
WebIDL::ExceptionOr<void> replace_sync(StringView text);

void for_each_effective_rule(TraversalOrder, Function<void(CSSRule const&)> const& callback) const;
void for_each_effective_style_rule(Function<void(CSSStyleRule const&)> const& callback) const;
void for_each_effective_style_producing_rule(Function<void(CSSRule const&)> 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<void(CSSKeyframesRule const&)> const& callback) const;
Expand Down
59 changes: 47 additions & 12 deletions Userland/Libraries/LibWeb/CSS/StyleComputer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
#include <LibWeb/CSS/CSSImportRule.h>
#include <LibWeb/CSS/CSSLayerBlockRule.h>
#include <LibWeb/CSS/CSSLayerStatementRule.h>
#include <LibWeb/CSS/CSSNestedDeclarations.h>
#include <LibWeb/CSS/CSSStyleRule.h>
#include <LibWeb/CSS/CSSTransition.h>
#include <LibWeb/CSS/Interpolation.h>
Expand Down Expand Up @@ -94,6 +95,33 @@ struct Traits<Web::CSS::FontFaceKey> : public DefaultTraits<Web::CSS::FontFaceKe

namespace Web::CSS {

PropertyOwningCSSStyleDeclaration const& MatchingRule::declaration() const
{
if (rule->type() == CSSRule::Type::Style)
return static_cast<CSSStyleRule const&>(*rule).declaration();
if (rule->type() == CSSRule::Type::NestedDeclarations)
return static_cast<CSSNestedDeclarations const&>(*rule).declaration();
VERIFY_NOT_REACHED();
}

SelectorList const& MatchingRule::absolutized_selectors() const
{
if (rule->type() == CSSRule::Type::Style)
return static_cast<CSSStyleRule const&>(*rule).absolutized_selectors();
if (rule->type() == CSSRule::Type::NestedDeclarations)
return static_cast<CSSStyleRule const&>(*rule->parent_rule()).absolutized_selectors();
VERIFY_NOT_REACHED();
}

FlyString const& MatchingRule::qualified_layer_name() const
{
if (rule->type() == CSSRule::Type::Style)
return static_cast<CSSStyleRule const&>(*rule).qualified_layer_name();
if (rule->type() == CSSRule::Type::NestedDeclarations)
return static_cast<CSSStyleRule const&>(*rule->parent_rule()).qualified_layer_name();
VERIFY_NOT_REACHED();
}

static DOM::Element const* element_to_inherit_style_from(DOM::Element const*, Optional<CSS::Selector::PseudoElement::Type>);

StyleComputer::StyleComputer(DOM::Document& document)
Expand Down Expand Up @@ -331,7 +359,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;
}
Expand Down Expand Up @@ -427,7 +455,7 @@ Vector<MatchingRule> 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;
Expand All @@ -454,7 +482,7 @@ Vector<MatchingRule> 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))
Expand All @@ -471,8 +499,8 @@ Vector<MatchingRule> StyleComputer::collect_matching_rules(DOM::Element const& e
static void sort_matching_rules(Vector<MatchingRule>& 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) {
Expand Down Expand Up @@ -882,20 +910,20 @@ void StyleComputer::set_all_properties(DOM::Element& element, Optional<CSS::Sele
void StyleComputer::cascade_declarations(StyleProperties& style, DOM::Element& element, Optional<CSS::Selector::PseudoElement::Type> pseudo_element, Vector<MatchingRule> 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;
}

auto property_value = property.value;
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);
}
}

Expand Down Expand Up @@ -924,7 +952,7 @@ static void cascade_custom_properties(DOM::Element& element, Optional<CSS::Selec
{
size_t needed_capacity = 0;
for (auto const& matching_rule : matching_rules)
needed_capacity += matching_rule.rule->declaration().custom_properties().size();
needed_capacity += matching_rule.declaration().custom_properties().size();

if (!pseudo_element.has_value()) {
if (auto const inline_style = element.inline_style())
Expand All @@ -934,7 +962,7 @@ static void cascade_custom_properties(DOM::Element& element, Optional<CSS::Selec
custom_properties.ensure_capacity(custom_properties.size() + needed_capacity);

for (auto const& matching_rule : matching_rules) {
for (auto const& it : matching_rule.rule->declaration().custom_properties()) {
for (auto const& it : matching_rule.declaration().custom_properties()) {
auto style_value = it.value.value;
if (style_value->is_revert_layer())
continue;
Expand Down Expand Up @@ -2404,9 +2432,16 @@ NonnullOwnPtr<StyleComputer::RuleCache> StyleComputer::make_rule_cache_for_casca
size_t style_sheet_index = 0;
for_each_stylesheet(cascade_origin, [&](auto& sheet, JS::GCPtr<DOM::ShadowRoot> 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<CSSStyleRule const&>(rule).absolutized_selectors();
if (rule.type() == CSSRule::Type::NestedDeclarations)
return static_cast<CSSStyleRule const&>(*rule.parent_rule()).absolutized_selectors();
VERIFY_NOT_REACHED();
}();
for (CSS::Selector const& selector : absolutized_selectors) {
MatchingRule matching_rule {
shadow_root,
&rule,
Expand Down
7 changes: 6 additions & 1 deletion Userland/Libraries/LibWeb/CSS/StyleComputer.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ enum class CascadeOrigin : u8 {

struct MatchingRule {
JS::GCPtr<DOM::ShadowRoot const> shadow_root;
JS::GCPtr<CSSStyleRule const> rule;
JS::GCPtr<CSSRule const> rule; // Either CSSStyleRule or CSSNestedDeclarations
JS::GCPtr<CSSStyleSheet const> sheet;
size_t style_sheet_index { 0 };
size_t rule_index { 0 };
Expand All @@ -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 {
Expand Down

0 comments on commit f616bda

Please sign in to comment.