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.
This commit is contained in:
parent
53f99e51f8
commit
e4245dc39e
Notes:
github-actions[bot]
2024-10-17 18:57:04 +00:00
Author: https://github.com/AtkinsSJ Commit: https://github.com/LadybirdBrowser/ladybird/commit/e4245dc39e6 Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/1842 Reviewed-by: https://github.com/awesomekling
6 changed files with 78 additions and 17 deletions
12
Tests/LibWeb/Ref/css-nested-declarations.html
Normal file
12
Tests/LibWeb/Ref/css-nested-declarations.html
Normal file
|
@ -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>
|
|
@ -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>
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -57,7 +57,7 @@ public:
|
|||
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;
|
||||
|
|
|
@ -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>
|
||||
|
@ -96,6 +97,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)
|
||||
|
@ -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<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;
|
||||
|
@ -461,7 +489,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))
|
||||
|
@ -478,8 +506,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) {
|
||||
|
@ -889,12 +917,12 @@ 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;
|
||||
}
|
||||
|
||||
|
@ -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, 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())
|
||||
|
@ -941,7 +969,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;
|
||||
|
@ -2416,9 +2444,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,
|
||||
|
|
|
@ -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 };
|
||||
|
@ -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 {
|
||||
|
|
Loading…
Add table
Reference in a new issue