瀏覽代碼

LibWeb/CSS: Implement revert-layer

With the introduction of the cascade layer, the 5th CSS-wide keyword,
`revert-layer`, has been added.
Annya 10 月之前
父節點
當前提交
bea7eec518

+ 1 - 0
Meta/Lagom/Tools/CodeGenerators/LibWeb/GenerateCSSKeyword.cpp

@@ -77,6 +77,7 @@ inline bool is_css_wide_keyword(StringView name)
     return name.equals_ignoring_ascii_case("inherit"sv)
         || name.equals_ignoring_ascii_case("initial"sv)
         || name.equals_ignoring_ascii_case("revert"sv)
+        || name.equals_ignoring_ascii_case("revert-layer"sv)
         || name.equals_ignoring_ascii_case("unset"sv);
 }
 

+ 4 - 0
Tests/LibWeb/Text/expected/css/revert-layer.txt

@@ -0,0 +1,4 @@
+    PASS: revert to base (first target)
+PASS: revert to base (second target)
+PASS: double revert (first target)
+PASS: double revert (second target)

+ 47 - 0
Tests/LibWeb/Text/input/css/revert-layer.html

@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<target class="first"></target>
+<target class="second"></target>
+<script src="../include.js"></script>
+<script>
+    test(() => {
+        // In all test cases, the rule specified as "color: green" should win.
+        var testCases = [
+            {
+                title: 'revert to base',
+                style: `
+                    @layer base, special;
+                    @layer special { target { color: revert-layer; } target.second { color: green; } }
+                    @layer base { target { color: green; } target.second { color: red; } }
+                `,
+            },
+            {
+                title: 'double revert',
+                style: `
+                    @layer layer1, layer2, layer3;
+                    @layer layer3 { target { color: revert-layer; } target.second { color: revert-layer; } }
+                    @layer layer2 { target { color: revert-layer; } target.second { color: green; } }
+                    @layer layer1 { target { color: green; } target.second { color: revert-layer; } }
+                `,
+            },
+        ];
+
+        for (let testCase of testCases) {
+            const styleElement = document.createElement('style');
+            styleElement.textContent = testCase['style'];
+            document.head.append(styleElement);
+
+            var targets = document.querySelectorAll('target');
+            for (let target of targets) {
+                let actual = window.getComputedStyle(target).color;
+                if (actual === 'rgb(0, 128, 0)') {
+                    println(`PASS: ${testCase['title']} (${target.classList[0]} target)`);
+                } else {
+                    println(`FAIL: ${testCase['title']} (${target.classList[0]} target) - Expected 'rgb(0, 128, 0)', got '${actual}'`);
+                }
+            }
+
+            styleElement.remove();
+        }
+
+    });
+</script>

+ 2 - 1
Userland/Libraries/LibWeb/CSS/CSSStyleValue.h

@@ -326,10 +326,11 @@ public:
 
     // https://www.w3.org/TR/css-values-4/#common-keywords
     // https://drafts.csswg.org/css-cascade-4/#valdef-all-revert
-    bool is_css_wide_keyword() const { return is_inherit() || is_initial() || is_revert() || is_unset(); }
+    bool is_css_wide_keyword() const { return is_inherit() || is_initial() || is_revert() || is_unset() || is_revert_layer(); }
     bool is_inherit() const { return to_keyword() == Keyword::Inherit; }
     bool is_initial() const { return to_keyword() == Keyword::Initial; }
     bool is_revert() const { return to_keyword() == Keyword::Revert; }
+    bool is_revert_layer() const { return to_keyword() == Keyword::RevertLayer; }
     bool is_unset() const { return to_keyword() == Keyword::Unset; }
 
     bool has_auto() const;

+ 1 - 0
Userland/Libraries/LibWeb/CSS/Keywords.json

@@ -293,6 +293,7 @@
   "repeat-y",
   "reverse",
   "revert",
+  "revert-layer",
   "ridge",
   "right",
   "round",

+ 4 - 1
Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp

@@ -1755,7 +1755,10 @@ RefPtr<CSSStyleValue> Parser::parse_builtin_value(TokenStream<ComponentValue>& t
             transaction.commit();
             return CSSKeywordValue::create(Keyword::Revert);
         }
-        // FIXME: Implement `revert-layer` from CSS-CASCADE-5.
+        if (ident.equals_ignoring_ascii_case("revert-layer"sv)) {
+            transaction.commit();
+            return CSSKeywordValue::create(Keyword::RevertLayer);
+        }
     }
 
     return nullptr;

+ 7 - 3
Userland/Libraries/LibWeb/CSS/StyleComputer.cpp

@@ -886,7 +886,7 @@ void StyleComputer::for_each_property_expanding_shorthands(PropertyID property_i
 void StyleComputer::set_property_expanding_shorthands(StyleProperties& style, CSS::PropertyID property_id, CSSStyleValue const& value, CSS::CSSStyleDeclaration const* declaration, StyleProperties const& style_for_revert, Important important)
 {
     for_each_property_expanding_shorthands(property_id, value, AllowUnresolved::No, [&](PropertyID shorthand_id, CSSStyleValue const& shorthand_value) {
-        if (shorthand_value.is_revert()) {
+        if (shorthand_value.is_revert() || shorthand_value.is_revert_layer()) {
             auto const& property_in_previous_cascade_origin = style_for_revert.m_data->m_property_values[to_underlying(shorthand_id)];
             if (property_in_previous_cascade_origin) {
                 style.set_property(shorthand_id, *property_in_previous_cascade_origin, StyleProperties::Inherited::No, important);
@@ -906,7 +906,7 @@ void StyleComputer::set_all_properties(DOM::Element& element, Optional<CSS::Sele
     for (auto i = to_underlying(CSS::first_longhand_property_id); i <= to_underlying(CSS::last_longhand_property_id); ++i) {
         auto property_id = (CSS::PropertyID)i;
 
-        if (value.is_revert()) {
+        if (value.is_revert() || value.is_revert_layer()) {
             style.revert_property(property_id, style_for_revert);
             continue;
         }
@@ -997,8 +997,12 @@ 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.rule->declaration().custom_properties()) {
+            auto style_value = it.value.value;
+            if (style_value->is_revert_layer())
+                continue;
             custom_properties.set(it.key, it.value);
+        }
     }
 
     if (!pseudo_element.has_value()) {

+ 4 - 0
Userland/Libraries/LibWeb/CSS/StyleValues/CSSKeywordValue.h

@@ -33,6 +33,10 @@ public:
             static ValueComparingNonnullRefPtr<CSSKeywordValue> const revert_instance = adopt_ref(*new (nothrow) CSSKeywordValue(Keyword::Revert));
             return revert_instance;
         }
+        case Keyword::RevertLayer: {
+            static ValueComparingNonnullRefPtr<CSSKeywordValue> const revert_layer_instance = adopt_ref(*new (nothrow) CSSKeywordValue(Keyword::RevertLayer));
+            return revert_layer_instance;
+        }
         case Keyword::Unset: {
             static ValueComparingNonnullRefPtr<CSSKeywordValue> const unset_instance = adopt_ref(*new (nothrow) CSSKeywordValue(Keyword::Unset));
             return unset_instance;