Implement initialValue support for CSS Custom Properties and Values API
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 22 Sep 2018 01:03:48 +0000 (01:03 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 22 Sep 2018 01:03:48 +0000 (01:03 +0000)
https://bugs.webkit.org/show_bug.cgi?id=189819

Patch by Justin Michaud <justin_michaud@apple.com> on 2018-09-21
Reviewed by Simon Fraser.

Source/WebCore:

* css/CSSComputedStyleDeclaration.cpp:
(WebCore::ComputedStyleExtractor::customPropertyValue):
* css/CSSCustomPropertyValue.cpp:
(WebCore::CSSCustomPropertyValue::resolveVariableReferences const):
* css/CSSCustomPropertyValue.h:
* css/CSSRegisteredCustomProperty.h:
* css/CSSVariableData.cpp:
(WebCore::CSSVariableData::resolveVariableFallback const):
(WebCore::CSSVariableData::resolveVariableReference const):
(WebCore::CSSVariableData::resolveVariableReferences const):
(WebCore::CSSVariableData::resolveTokenRange const):
* css/CSSVariableData.h:
* css/DOMCSSRegisterCustomProperty.cpp:
(WebCore::DOMCSSRegisterCustomProperty::registerProperty):
* css/DOMCSSRegisterCustomProperty.h:
* css/DOMCSSRegisterCustomProperty.idl:
* css/StyleResolver.cpp:
(WebCore::StyleResolver::resolvedVariableValue):
(WebCore::StyleResolver::applyCascadedProperties):
* css/parser/CSSParser.cpp:
(WebCore::CSSParser::parseValueWithVariableReferences):
* css/parser/CSSParser.h:
* dom/Document.h:
(WebCore::Document::getCSSRegisteredCustomPropertySet const):
* rendering/style/RenderStyle.cpp:
(WebCore::RenderStyle::checkVariablesInCustomProperties):
* rendering/style/RenderStyle.h:

LayoutTests:

* css-custom-properties-api/initialValue-expected.html: Added.
* css-custom-properties-api/initialValue.html: Added.
* css-custom-properties-api/initialValueJS-expected.txt: Added.
* css-custom-properties-api/initialValueJS.html: Added.
* css-custom-properties-api/registerProperty-expected.txt:
* css-custom-properties-api/registerProperty.html:
* platform/win/TestExpectations:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@236379 268f45cc-cd09-0410-ab3c-d52691b4dbfc

24 files changed:
LayoutTests/ChangeLog
LayoutTests/css-custom-properties-api/initialValue-expected.html [new file with mode: 0644]
LayoutTests/css-custom-properties-api/initialValue.html [new file with mode: 0644]
LayoutTests/css-custom-properties-api/initialValueJS-expected.txt [new file with mode: 0644]
LayoutTests/css-custom-properties-api/initialValueJS.html [new file with mode: 0644]
LayoutTests/css-custom-properties-api/registerProperty-expected.txt
LayoutTests/css-custom-properties-api/registerProperty.html
LayoutTests/platform/win/TestExpectations
Source/WebCore/ChangeLog
Source/WebCore/css/CSSComputedStyleDeclaration.cpp
Source/WebCore/css/CSSCustomPropertyValue.cpp
Source/WebCore/css/CSSCustomPropertyValue.h
Source/WebCore/css/CSSRegisteredCustomProperty.h
Source/WebCore/css/CSSVariableData.cpp
Source/WebCore/css/CSSVariableData.h
Source/WebCore/css/DOMCSSRegisterCustomProperty.cpp
Source/WebCore/css/DOMCSSRegisterCustomProperty.h
Source/WebCore/css/DOMCSSRegisterCustomProperty.idl
Source/WebCore/css/StyleResolver.cpp
Source/WebCore/css/parser/CSSParser.cpp
Source/WebCore/css/parser/CSSParser.h
Source/WebCore/dom/Document.h
Source/WebCore/rendering/style/RenderStyle.cpp
Source/WebCore/rendering/style/RenderStyle.h

index 48f36e5..f6139aa 100644 (file)
@@ -1,3 +1,18 @@
+2018-09-21  Justin Michaud  <justin_michaud@apple.com>
+
+        Implement initialValue support for CSS Custom Properties and Values API
+        https://bugs.webkit.org/show_bug.cgi?id=189819
+
+        Reviewed by Simon Fraser.
+
+        * css-custom-properties-api/initialValue-expected.html: Added.
+        * css-custom-properties-api/initialValue.html: Added.
+        * css-custom-properties-api/initialValueJS-expected.txt: Added.
+        * css-custom-properties-api/initialValueJS.html: Added.
+        * css-custom-properties-api/registerProperty-expected.txt:
+        * css-custom-properties-api/registerProperty.html:
+        * platform/win/TestExpectations:
+
 2018-09-21  Ryan Haddad  <ryanhaddad@apple.com>
 
         [macOS EWS] Layout test accessibility/smart-invert-reference.html is a flaky failure
diff --git a/LayoutTests/css-custom-properties-api/initialValue-expected.html b/LayoutTests/css-custom-properties-api/initialValue-expected.html
new file mode 100644 (file)
index 0000000..fed2e5e
--- /dev/null
@@ -0,0 +1,88 @@
+<html>
+<head>
+  <style>
+    html {
+      background: grey;
+    }
+    .parent1 {
+      width: 500px;
+      background: blue;
+    }
+    .child1 {
+      background: green;
+      width: 100px;
+    }
+
+    .parent2 {
+      width: 500px;
+      background: blue;
+    }
+    .child2 {
+      background: green;
+      width: 200px;
+    }
+
+    .parent3 {
+      width: 500px;
+      background: blue;
+    }
+    .child3 {
+      background: green;
+      width: 500px;
+    }
+
+    .parent4 {
+      width: 500px;
+      background: blue;
+    }
+    .child4 {
+      background: green;
+      width: 200px;
+    }
+
+    .parent5 {
+      width: 500px;
+      background: blue;
+    }
+    .child5 {
+      background: green;
+      width: 300px;
+    }
+
+    .parent6 {
+      width: 500px;
+      background: blue;
+    }
+    .child6 {
+      background: green;
+      width: 500px;
+    }
+
+    .parent7 {
+      width: 500px;
+      background: blue;
+    }
+    .child7 {
+      background: green;
+      width: 500px;
+    }
+  </style>
+</head>
+<body>
+  <p> Single values </p>
+  <p> Specified </p>
+  <div class="parent1"><div class="child1"><p>100px green</p></div> </div>
+  <p> Not specified, use initial value </p>
+  <div class="parent2"><div class="child2"><p>200px green</p></div> </div>
+  <p> Not specified, but not registered so use initial value for width </p>
+  <div class="parent3"><div class="child3"><p>500px green</p></div> </div>
+  <p> Has a fallback for registered property </p>
+  <div class="parent4"><div class="child4"><p>200px green</p></div> </div>
+  <p> Has a fallback for unregistered property </p>
+  <div class="parent5"><div class="child5"><p>300px green</p></div> </div>
+  <p> Having a fallback for a registered property that is a variable should not work</p>
+  <div class="parent6"><div class="child6"><p>500px green</p></div> </div>
+  <p> Having a variable in the initial value should not work</p>
+  <div class="parent7"><div class="child7"><p>500px green</p></div> </div>
+</body>
+</html>
diff --git a/LayoutTests/css-custom-properties-api/initialValue.html b/LayoutTests/css-custom-properties-api/initialValue.html
new file mode 100644 (file)
index 0000000..7721f1e
--- /dev/null
@@ -0,0 +1,113 @@
+<html>
+<head>
+  <style>
+    html {
+      background: grey;
+    }
+    .parent1 {
+      width: 500px;
+      background: blue;
+    }
+    .child1 {
+      background: green;
+      --my-custom-prop: 100px;
+      width: var(--my-custom-prop);
+    }
+
+    .parent2 {
+      width: 500px;
+      background: blue;
+    }
+    .child2 {
+      background: green;
+      width: var(--my-custom-prop2);
+    }
+
+    .parent3 {
+      width: 500px;
+      background: blue;
+    }
+    .child3 {
+      background: green;
+      width: var(--my-custom-prop3);
+    }
+
+    .parent4 {
+      width: 500px;
+      background: blue;
+    }
+    .child4 {
+      background: green;
+      width: var(--my-custom-prop2, 300px);
+    }
+
+    .parent5 {
+      width: 500px;
+      background: blue;
+    }
+    .child5 {
+      background: green;
+      width: var(--my-custom-prop3, 300px);
+    }
+
+    .parent6 {
+      width: 500px;
+      background: blue;
+    }
+    .child6 {
+      background: green;
+      --my-custom-prop: 100px;
+      width: var(--my-custom-prop3, --my-custom-prop1);
+    }
+
+    .parent7 {
+      width: 500px;
+      background: blue;
+    }
+    .child7 {
+      background: green;
+      --my-custom-prop: 100px;
+      width: var(--my-custom-prop4);
+    }
+  </style>
+  <script>
+    CSS.registerProperty({
+      name: '--my-custom-prop',
+      syntax: '<length>',
+      inherits: false,
+      initialValue: '200px'
+    })
+
+    CSS.registerProperty({
+      name: '--my-custom-prop2',
+      syntax: '<length>',
+      inherits: false,
+      initialValue: '200px'
+    })
+
+    CSS.registerProperty({
+      name: '--my-custom-prop4',
+      syntax: '<length>',
+      inherits: false,
+      initialValue: 'var(--my-custom-prop)'
+    })
+  </script>
+</head>
+<body>
+  <p> Single values </p>
+  <p> Specified </p>
+  <div class="parent1"><div class="child1"><p>100px green</p></div> </div>
+  <p> Not specified, use initial value </p>
+  <div class="parent2"><div class="child2"><p>200px green</p></div> </div>
+  <p> Not specified, but not registered so use initial value for width </p>
+  <div class="parent3"><div class="child3"><p>500px green</p></div> </div>
+  <p> Has a fallback for registered property </p>
+  <div class="parent4"><div class="child4"><p>200px green</p></div> </div>
+  <p> Has a fallback for unregistered property </p>
+  <div class="parent5"><div class="child5"><p>300px green</p></div> </div>
+  <p> Having a fallback for a registered property that is a variable should not work</p>
+  <div class="parent6"><div class="child6"><p>500px green</p></div> </div>
+  <p> Having a variable in the initial value should not work</p>
+  <div class="parent7"><div class="child7"><p>500px green</p></div> </div>
+</body>
+</html>
diff --git a/LayoutTests/css-custom-properties-api/initialValueJS-expected.txt b/LayoutTests/css-custom-properties-api/initialValueJS-expected.txt
new file mode 100644 (file)
index 0000000..7965f37
--- /dev/null
@@ -0,0 +1,27 @@
+Specified
+
+100px green
+
+Not specified, use initial value
+
+200px green
+
+Not specified, but not registered so use initial value for width
+
+500px green
+
+Has a fallback for unregistered property
+
+300px green
+
+Has a fallback for a registered property
+
+200px green
+
+
+PASS Registration is successful 
+PASS JS Attributes are valid for element 1 
+PASS JS Attributes are valid for element 2 
+PASS JS Attributes are valid for element 3 
+PASS JS Attributes are valid for element 4 and 5 
+
diff --git a/LayoutTests/css-custom-properties-api/initialValueJS.html b/LayoutTests/css-custom-properties-api/initialValueJS.html
new file mode 100644 (file)
index 0000000..fefa061
--- /dev/null
@@ -0,0 +1,107 @@
+<!DOCTYPE html><!-- webkit-test-runner [ experimental:CSSCustomPropertiesAndValuesEnabled=true ] -->
+<!-- https://chromium.googlesource.com/chromium/src/+/01ce431409e3a019858677626a983c55168da6dc/third_party/WebKit/LayoutTests/custom-properties/register-property.html -->
+<script src="../resources/testharness.js"></script>
+<script src="../resources/testharnessreport.js"></script>
+
+<style>
+  #parent1 {
+    width: 500px;
+    background: blue;
+  }
+  #child1 {
+    background: green;
+    --my-custom-prop: 100px;
+    width: var(--my-custom-prop);
+  }
+
+  #parent2 {
+    width: 500px;
+    background: blue;
+  }
+  #child2 {
+    background: green;
+    width: var(--my-custom-prop2);
+  }
+
+  #parent3 {
+    width: 500px;
+    background: blue;
+  }
+  #child3 {
+    background: green;
+    width: var(--my-custom-prop3);
+  }
+
+  #parent4 {
+    width: 500px;
+    background: blue;
+  }
+  #child4 {
+    background: green;
+    width: var(--my-custom-prop3, 300px);
+  }
+
+  #parent5 {
+    width: 500px;
+    background: blue;
+  }
+  #child5 {
+    background: green;
+    width: var(--my-custom-prop2, 300px);
+  }
+</style>
+<div>
+  <p> Specified </p>
+  <div id="parent1"><div id="child1"><p>100px green</p></div> </div>
+  <p> Not specified, use initial value </p>
+  <div id="parent2"><div id="child2"><p>200px green</p></div> </div>
+  <p> Not specified, but not registered so use initial value for width </p>
+  <div id="parent3"><div id="child3"><p>500px green</p></div> </div>
+  <p> Has a fallback for unregistered property </p>
+  <div id="parent4"><div id="child4"><p>300px green</p></div> </div>
+  <p> Has a fallback for a registered property </p>
+  <div id="parent5"><div id="child5"><p>200px green</p></div> </div>
+</div>
+<script>
+
+function test_prop(id, prop, expected) {
+  assert_equals(window.getComputedStyle(document.getElementById(id)).getPropertyValue(prop).trim(), expected);
+}
+
+test(function() {
+  CSS.registerProperty({
+    name: '--my-custom-prop',
+    syntax: '<length>',
+    inherits: false,
+    initialValue: '200px'
+  })
+
+  CSS.registerProperty({
+    name: '--my-custom-prop2',
+    syntax: '<length>',
+    inherits: false,
+    initialValue: '200px'
+  })
+}, "Registration is successful");
+test(function() {
+  test_prop('child1', 'width', '100px');
+  test_prop('child1', '--my-custom-prop', '100px');
+  test_prop('child1', '--my-custom-prop2', '200px');
+}, "JS Attributes are valid for element 1");
+test(function() {
+  test_prop('child2', 'width', '200px');
+  test_prop('child2', '--my-custom-prop', '200px');
+  test_prop('child2', '--my-custom-prop2', '200px');
+}, "JS Attributes are valid for element 2");
+test(function() {
+  test_prop('child3', 'width', '500px');
+  test_prop('child3', '--my-custom-prop3', '');
+  test_prop('child3', '--my-custom-prop2', '200px');
+}, "JS Attributes are valid for element 3");
+test(function() {
+  test_prop('child4', 'width', '300px');
+  test_prop('child5', 'width', '200px');
+  test_prop('child4', '--my-custom-prop2', '200px');
+  test_prop('child5', '--my-custom-prop2', '200px');
+}, "JS Attributes are valid for element 4 and 5");
+</script>
index cfcfece..b3082e1 100644 (file)
@@ -1,5 +1,6 @@
 
 PASS registerProperty requires a Dictionary type 
 PASS registerProperty requires a name matching <custom-property-name> 
-PASS registerProperty only allows omitting initialValue is syntax is '*' 
+PASS registerProperty always allows omitting initialValue and syntax, requires name and inherits 
+PASS registerProperty requires inherits and name 
 
index d6e4e5c..1f57666 100644 (file)
@@ -18,8 +18,6 @@ test(function() {
     CSS.registerProperty({name: '--name2, no need for escapes', inherits: false});
     CSS.registerProperty({name: ['--name', 3], inherits: false});
     // Invalid property names
-    assert_throws(new TypeError(), () => CSS.registerProperty({}));
-    assert_throws(new TypeError(), () => CSS.registerProperty({name: '--no-inherits'}));
     //assert_throws(new SyntaxError(), () => CSS.registerProperty({name: 'no-leading-dash', inherits: false}));
     //assert_throws(new SyntaxError(), () => CSS.registerProperty({name: '', inherits: false}));
     //assert_throws(new SyntaxError(), () => CSS.registerProperty({name: '\\--name', inherits: false}));
@@ -27,7 +25,16 @@ test(function() {
 test(function() {
     CSS.registerProperty({name: '--syntax-test-1', inherits: false, syntax: '*'});
     CSS.registerProperty({name: '--syntax-test-2', inherits: false, syntax: ' * '});
-    /*assert_throws(new SyntaxError(),
-        () => CSS.registerProperty({name: '--syntax-test-3', syntax: 'length'}));*/
-}, "registerProperty only allows omitting initialValue is syntax is '*'");
+    CSS.registerProperty({name: '--syntax-test-3', inherits: false, initialValue: '500px'});
+    assert_throws(new TypeError(), () => CSS.registerProperty({}));
+    assert_throws(new TypeError(), () => CSS.registerProperty({name: '--no-inherits'}));
+    assert_throws(new TypeError(), () => CSS.registerProperty({inherits: false}));
+    // Repeated name
+    assert_throws(null,
+        () => CSS.registerProperty({name: '--syntax-test-3', inherits: false, initialValue: '500px'}));
+}, "registerProperty always allows omitting initialValue and syntax, requires name and inherits");
+test(function() {
+    CSS.registerProperty({name: '--syntax-test-4', inherits: false, syntax: '*'});
+    CSS.registerProperty({name: '--syntax-test-5', inherits: false, syntax: ' * '});
+}, "registerProperty requires inherits and name");
 </script>
index 28c661c..b9993fc 100644 (file)
@@ -4160,6 +4160,9 @@ http/tests/security/mixedContent/websocket [ Skip ]
 imported/blink/http/tests/websocket [ Skip ]
 imported/blink/http/tests/security/mixedContent/websocket [ Skip ]
 
+# Feature flag only enabled for wk2
+css-custom-properties-api [ Skip ]
+
 editing/pasteboard/drag-and-drop-color-input-events.html [ Skip ]
 
 webkit.org/b/188853 http/tests/security/contentSecurityPolicy/userAgentShadowDOM/allow-video.html [ Crash Pass ]
index 9d815f6..0b0ac13 100644 (file)
@@ -1,3 +1,38 @@
+2018-09-21  Justin Michaud  <justin_michaud@apple.com>
+
+        Implement initialValue support for CSS Custom Properties and Values API
+        https://bugs.webkit.org/show_bug.cgi?id=189819
+
+        Reviewed by Simon Fraser.
+
+        * css/CSSComputedStyleDeclaration.cpp:
+        (WebCore::ComputedStyleExtractor::customPropertyValue):
+        * css/CSSCustomPropertyValue.cpp:
+        (WebCore::CSSCustomPropertyValue::resolveVariableReferences const):
+        * css/CSSCustomPropertyValue.h:
+        * css/CSSRegisteredCustomProperty.h:
+        * css/CSSVariableData.cpp:
+        (WebCore::CSSVariableData::resolveVariableFallback const):
+        (WebCore::CSSVariableData::resolveVariableReference const):
+        (WebCore::CSSVariableData::resolveVariableReferences const):
+        (WebCore::CSSVariableData::resolveTokenRange const):
+        * css/CSSVariableData.h:
+        * css/DOMCSSRegisterCustomProperty.cpp:
+        (WebCore::DOMCSSRegisterCustomProperty::registerProperty):
+        * css/DOMCSSRegisterCustomProperty.h:
+        * css/DOMCSSRegisterCustomProperty.idl:
+        * css/StyleResolver.cpp:
+        (WebCore::StyleResolver::resolvedVariableValue):
+        (WebCore::StyleResolver::applyCascadedProperties):
+        * css/parser/CSSParser.cpp:
+        (WebCore::CSSParser::parseValueWithVariableReferences):
+        * css/parser/CSSParser.h:
+        * dom/Document.h:
+        (WebCore::Document::getCSSRegisteredCustomPropertySet const):
+        * rendering/style/RenderStyle.cpp:
+        (WebCore::RenderStyle::checkVariablesInCustomProperties):
+        * rendering/style/RenderStyle.h:
+
 2018-09-21  Dean Jackson  <dino@apple.com>
 
         Add PointerEvent, plus feature flag, plus Web Platform Tests
index db9c6d7..b7b0a48 100644 (file)
@@ -2624,7 +2624,15 @@ RefPtr<CSSValue> ComputedStyleExtractor::customPropertyValue(const String& prope
     if (!style)
         return nullptr;
 
-    return style->customProperties().get(propertyName);
+    auto* value = style->customProperties().get(propertyName);
+    if (value)
+        return value;
+
+    auto* registered = styledElement->document().getCSSRegisteredCustomPropertySet().get(propertyName);
+    if (registered && registered->initialValue)
+        return registered->initialValue;
+
+    return nullptr;
 }
 
 String ComputedStyleExtractor::customPropertyText(const String& propertyName)
index 42b97c1..16880ee 100644 (file)
@@ -37,14 +37,14 @@ bool CSSCustomPropertyValue::checkVariablesForCycles(const AtomicString& name, C
     return true;
 }
 
-void CSSCustomPropertyValue::resolveVariableReferences(const CustomPropertyValueMap& customProperties, Vector<Ref<CSSCustomPropertyValue>>& resolvedValues) const
+void CSSCustomPropertyValue::resolveVariableReferences(const CustomPropertyValueMap& customProperties, const CSSRegisteredCustomPropertySet& registeredProperties, Vector<Ref<CSSCustomPropertyValue>>& resolvedValues) const
 {
     ASSERT(containsVariables());
     if (!m_value)
         return;
     
     ASSERT(m_value->needsVariableResolution());
-    RefPtr<CSSVariableData> resolvedData = m_value->resolveVariableReferences(customProperties);
+    RefPtr<CSSVariableData> resolvedData = m_value->resolveVariableReferences(customProperties, registeredProperties);
     if (resolvedData)
         resolvedValues.append(CSSCustomPropertyValue::createWithVariableData(m_name, resolvedData.releaseNonNull()));
     else
index 31958b2..aac11c1 100644 (file)
@@ -25,6 +25,7 @@
 
 #pragma once
 
+#include "CSSRegisteredCustomProperty.h"
 #include "CSSValue.h"
 #include "CSSVariableData.h"
 #include <wtf/RefPtr.h>
@@ -70,7 +71,7 @@ public:
     bool containsVariables() const { return m_containsVariables; }
     bool checkVariablesForCycles(const AtomicString& name, CustomPropertyValueMap&, HashSet<AtomicString>& seenProperties, HashSet<AtomicString>& invalidProperties) const;
 
-    void resolveVariableReferences(const CustomPropertyValueMap&, Vector<Ref<CSSCustomPropertyValue>>&) const;
+    void resolveVariableReferences(const CustomPropertyValueMap&, const CSSRegisteredCustomPropertySet&, Vector<Ref<CSSCustomPropertyValue>>&) const;
 
     CSSValueID valueID() const { return m_valueId; }
     CSSVariableData* value() const { return m_value.get(); }
index b05e2af..b2f9cdb 100644 (file)
 
 namespace WebCore {
 
+class CSSCustomPropertyValue;
+
 struct CSSRegisteredCustomProperty {
     const String name;
-    /* TODO syntax, inherits, initialValue */
+    /* TODO syntax, inherits */
+    const RefPtr<CSSCustomPropertyValue> initialValue;
 };
 
+using CSSRegisteredCustomPropertySet = HashMap<String, std::unique_ptr<CSSRegisteredCustomProperty>>;
+
 }
index 98c9d09..cdf0ca9 100644 (file)
@@ -125,16 +125,16 @@ bool CSSVariableData::checkVariablesForCyclesWithRange(CSSParserTokenRange range
     return true;
 }
 
-bool CSSVariableData::resolveVariableFallback(const CustomPropertyValueMap& customProperties, CSSParserTokenRange range, Vector<CSSParserToken>& result) const
+bool CSSVariableData::resolveVariableFallback(const CustomPropertyValueMap& customProperties, const CSSRegisteredCustomPropertySet& registeredProperties, CSSParserTokenRange range, Vector<CSSParserToken>& result) const
 {
     if (range.atEnd())
         return false;
     ASSERT(range.peek().type() == CommaToken);
     range.consume();
-    return resolveTokenRange(customProperties, range, result);
+    return resolveTokenRange(customProperties, registeredProperties, range, result);
 }
-    
-bool CSSVariableData::resolveVariableReference(const CustomPropertyValueMap& customProperties, CSSParserTokenRange range, Vector<CSSParserToken>& result) const
+
+bool CSSVariableData::resolveVariableReference(const CustomPropertyValueMap& customProperties, const CSSRegisteredCustomPropertySet& registeredProperties, CSSParserTokenRange range, Vector<CSSParserToken>& result) const
 {
     range.consumeWhitespace();
     ASSERT(range.peek().type() == IdentToken);
@@ -142,12 +142,18 @@ bool CSSVariableData::resolveVariableReference(const CustomPropertyValueMap& cus
     ASSERT(range.atEnd() || (range.peek().type() == CommaToken));
     
     RefPtr<CSSCustomPropertyValue> property = customProperties.get(variableName);
-    if (!property || !property->value())
-        return resolveVariableFallback(customProperties, range, result);
+    if (!property || !property->value()) {
+        auto* registered = registeredProperties.get(variableName);
+        if (registered && registered->initialValue)
+            property = registered->initialValue;
+        else
+            return resolveVariableFallback(customProperties, registeredProperties, range, result);
+    }
+    ASSERT(property);
     
     if (property->containsVariables()) {
         // FIXME: Avoid doing this work more than once.
-        RefPtr<CSSVariableData> resolvedData = property->value()->resolveVariableReferences(customProperties);
+        RefPtr<CSSVariableData> resolvedData = property->value()->resolveVariableReferences(customProperties, registeredProperties);
         if (!resolvedData)
             return false;
         result.appendVector(resolvedData->tokens());
@@ -157,21 +163,21 @@ bool CSSVariableData::resolveVariableReference(const CustomPropertyValueMap& cus
     return true;
 }
 
-RefPtr<CSSVariableData> CSSVariableData::resolveVariableReferences(const CustomPropertyValueMap& customProperties) const
+RefPtr<CSSVariableData> CSSVariableData::resolveVariableReferences(const CustomPropertyValueMap& customProperties, const CSSRegisteredCustomPropertySet& registeredProperties) const
 {
     Vector<CSSParserToken> resolvedTokens;
     CSSParserTokenRange range = m_tokens;
-    if (!resolveTokenRange(customProperties, range, resolvedTokens))
+    if (!resolveTokenRange(customProperties, registeredProperties, range, resolvedTokens))
         return nullptr;
     return CSSVariableData::createResolved(resolvedTokens, *this);
 }
     
-bool CSSVariableData::resolveTokenRange(const CustomPropertyValueMap& customProperties, CSSParserTokenRange range, Vector<CSSParserToken>& result) const
+bool CSSVariableData::resolveTokenRange(const CustomPropertyValueMap& customProperties, const CSSRegisteredCustomPropertySet& registeredProperties, CSSParserTokenRange range, Vector<CSSParserToken>& result) const
 {
     bool success = true;
     while (!range.atEnd()) {
         if (range.peek().functionId() == CSSValueVar || range.peek().functionId() == CSSValueEnv)
-            success &= resolveVariableReference(customProperties, range.consumeBlock(), result);
+            success &= resolveVariableReference(customProperties, registeredProperties, range.consumeBlock(), result);
         else
             result.append(range.consume());
     }
index c5e1d71..6223b4f 100644 (file)
@@ -31,6 +31,7 @@
 
 #include "CSSParserToken.h"
 #include "CSSParserTokenRange.h"
+#include "CSSRegisteredCustomProperty.h"
 #include <wtf/HashSet.h>
 #include <wtf/text/WTFString.h>
 
@@ -62,8 +63,8 @@ public:
 
     bool checkVariablesForCycles(const AtomicString& name, CustomPropertyValueMap&, HashSet<AtomicString>& seenProperties, HashSet<AtomicString>& invalidProperties) const;
 
-    RefPtr<CSSVariableData> resolveVariableReferences(const CustomPropertyValueMap& customProperties) const;
-    bool resolveTokenRange(const CustomPropertyValueMap&, CSSParserTokenRange, Vector<CSSParserToken>&) const;
+    RefPtr<CSSVariableData> resolveVariableReferences(const CustomPropertyValueMap& customProperties, const CSSRegisteredCustomPropertySet&) const;
+    bool resolveTokenRange(const CustomPropertyValueMap&, const CSSRegisteredCustomPropertySet&, CSSParserTokenRange, Vector<CSSParserToken>&) const;
 
 private:
     CSSVariableData(const CSSParserTokenRange&, bool needsVariableResolution);
@@ -82,8 +83,8 @@ private:
     template<typename CharacterType> void updateTokens(const CSSParserTokenRange&);
     
     bool checkVariablesForCyclesWithRange(CSSParserTokenRange, CustomPropertyValueMap&, HashSet<AtomicString>& seenProperties, HashSet<AtomicString>& invalidProperties) const;
-    bool resolveVariableReference(const CustomPropertyValueMap&, CSSParserTokenRange, Vector<CSSParserToken>&) const;
-    bool resolveVariableFallback(const CustomPropertyValueMap&, CSSParserTokenRange, Vector<CSSParserToken>&) const;
+    bool resolveVariableReference(const CustomPropertyValueMap&, const CSSRegisteredCustomPropertySet&, CSSParserTokenRange, Vector<CSSParserToken>&) const;
+    bool resolveVariableFallback(const CustomPropertyValueMap&, const CSSRegisteredCustomPropertySet&, CSSParserTokenRange, Vector<CSSParserToken>&) const;
 
     String m_backingString;
     Vector<CSSParserToken> m_tokens;
index 7cc532e..b1308a6 100644 (file)
 #include "config.h"
 #include "DOMCSSRegisterCustomProperty.h"
 
+#include "CSSCustomPropertyValue.h"
 #include "CSSRegisteredCustomProperty.h"
+#include "CSSTokenizer.h"
 #include "DOMCSSNamespace.h"
 #include "Document.h"
 #include <wtf/text/WTFString.h>
 
 namespace WebCore {
 
-void DOMCSSRegisterCustomProperty::registerProperty(Document& document, const DOMCSSCustomPropertyDescriptor& descriptor)
+ExceptionOr<void> DOMCSSRegisterCustomProperty::registerProperty(Document& document, const DOMCSSCustomPropertyDescriptor& descriptor)
 {
-    CSSRegisteredCustomProperty property { descriptor.name };
-    if (!document.registerCSSProperty(WTFMove(property))) {
-        /* TODO throw JS exception */
-        return;
-    }
+    CSSTokenizer tokenizer(descriptor.initialValue);
+    RefPtr<CSSCustomPropertyValue> initialValue;
+    if (!tokenizer.tokenRange().atEnd())
+        initialValue = CSSCustomPropertyValue::createWithVariableData(descriptor.name, CSSVariableData::create(tokenizer.tokenRange(), false));
+
+    CSSRegisteredCustomProperty property { descriptor.name, WTFMove(initialValue) };
+    if (!document.registerCSSProperty(WTFMove(property)))
+        return Exception { InvalidModificationError, "This property has already been registered." };
+
+    return { };
 }
 
 DOMCSSRegisterCustomProperty* DOMCSSRegisterCustomProperty::from(DOMCSSNamespace& css)
index d93b1dc..ee9988a 100644 (file)
@@ -26,6 +26,7 @@
 #pragma once
 
 #include "DOMCSSCustomPropertyDescriptor.h"
+#include "ExceptionOr.h"
 #include "Supplementable.h"
 
 namespace WebCore {
@@ -37,7 +38,7 @@ class DOMCSSRegisterCustomProperty final : public Supplement<DOMCSSNamespace> {
 public:
     explicit DOMCSSRegisterCustomProperty(DOMCSSNamespace&) { }
 
-    static void registerProperty(Document&, const DOMCSSCustomPropertyDescriptor&);
+    static ExceptionOr<void> registerProperty(Document&, const DOMCSSCustomPropertyDescriptor&);
 
 private:
     static DOMCSSRegisterCustomProperty* from(DOMCSSNamespace&);
index 421696b..5d9fb6c 100644 (file)
@@ -26,5 +26,5 @@
 [
     EnabledAtRuntime=CSSCustomPropertiesAndValues
 ] partial interface DOMCSSNamespace {
-    [CallWith=Document] static void registerProperty(DOMCSSCustomPropertyDescriptor descriptor);
+    [CallWith=Document, MayThrowException] static void registerProperty(DOMCSSCustomPropertyDescriptor descriptor);
 };
index a42aa25..4e011bf 100644 (file)
@@ -1708,7 +1708,7 @@ void StyleResolver::applyProperty(CSSPropertyID id, CSSValue* value, SelectorChe
 RefPtr<CSSValue> StyleResolver::resolvedVariableValue(CSSPropertyID propID, const CSSValue& value)
 {
     CSSParser parser(document());
-    return parser.parseValueWithVariableReferences(propID, value, m_state.style()->customProperties(), m_state.style()->direction(), m_state.style()->writingMode());
+    return parser.parseValueWithVariableReferences(propID, value, m_state.style()->customProperties(), document().getCSSRegisteredCustomPropertySet(), m_state.style()->direction(), m_state.style()->writingMode());
 }
 
 RefPtr<StyleImage> StyleResolver::styleImage(CSSValue& value)
@@ -2285,7 +2285,7 @@ void StyleResolver::applyCascadedProperties(CascadedProperties& cascade, int fir
     }
 
     if (firstProperty == CSSPropertyCustom)
-        m_state.style()->checkVariablesInCustomProperties();
+        m_state.style()->checkVariablesInCustomProperties(document().getCSSRegisteredCustomPropertySet());
 }
 
 } // namespace WebCore
index 729faed..dd6e4bd 100644 (file)
@@ -175,7 +175,7 @@ void CSSParser::parseDeclarationForInspector(const CSSParserContext& context, co
     CSSParserImpl::parseDeclarationListForInspector(string, context, observer);
 }
 
-RefPtr<CSSValue> CSSParser::parseValueWithVariableReferences(CSSPropertyID propID, const CSSValue& value, const CustomPropertyValueMap& customProperties, TextDirection direction, WritingMode writingMode)
+RefPtr<CSSValue> CSSParser::parseValueWithVariableReferences(CSSPropertyID propID, const CSSValue& value, const CustomPropertyValueMap& customProperties, const CSSRegisteredCustomPropertySet& registeredProperties, TextDirection direction, WritingMode writingMode)
 {
     if (value.isPendingSubstitutionValue()) {
         // FIXME: Should have a resolvedShorthands cache to stop this from being done
@@ -189,7 +189,7 @@ RefPtr<CSSValue> CSSParser::parseValueWithVariableReferences(CSSPropertyID propI
         ASSERT(variableData);
         
         Vector<CSSParserToken> resolvedTokens;
-        if (!variableData->resolveTokenRange(customProperties, variableData->tokens(), resolvedTokens))
+        if (!variableData->resolveTokenRange(customProperties, registeredProperties, variableData->tokens(), resolvedTokens))
             return nullptr;
         
         ParsedPropertyVector parsedProperties;
@@ -210,7 +210,7 @@ RefPtr<CSSValue> CSSParser::parseValueWithVariableReferences(CSSPropertyID propI
         ASSERT(variableData);
         
         Vector<CSSParserToken> resolvedTokens;
-        if (!variableData->resolveTokenRange(customProperties, variableData->tokens(), resolvedTokens))
+        if (!variableData->resolveTokenRange(customProperties, registeredProperties, variableData->tokens(), resolvedTokens))
             return nullptr;
         
         return CSSPropertyParser::parseSingleValue(propID, resolvedTokens, m_context);
index ca5faa4..f74fb48 100644 (file)
@@ -23,6 +23,7 @@
 #pragma once
 
 #include "CSSParserContext.h"
+#include "CSSRegisteredCustomProperty.h"
 #include "CSSValue.h"
 #include "WritingMode.h"
 #include <wtf/text/WTFString.h>
@@ -75,7 +76,7 @@ public:
 
     void parseSelector(const String&, CSSSelectorList&);
 
-    RefPtr<CSSValue> parseValueWithVariableReferences(CSSPropertyID, const CSSValue&, const CustomPropertyValueMap& customProperties, TextDirection, WritingMode);
+    RefPtr<CSSValue> parseValueWithVariableReferences(CSSPropertyID, const CSSValue&, const CustomPropertyValueMap&, const CSSRegisteredCustomPropertySet&, TextDirection, WritingMode);
 
     static Color parseColor(const String&, bool strict = false);
     static Color parseSystemColor(const String&, const CSSParserContext*);
index ffce474..8d6c5bc 100644 (file)
@@ -1492,6 +1492,7 @@ public:
     void updateMainArticleElementAfterLayout();
     bool hasMainArticleElement() const { return !!m_mainArticleElement; }
 
+    const CSSRegisteredCustomPropertySet& getCSSRegisteredCustomPropertySet() const { return m_CSSRegisteredPropertySet; }
     bool registerCSSProperty(CSSRegisteredCustomProperty&&);
 
     void setAsRunningUserScripts() { m_isRunningUserScripts = true; }
@@ -2026,7 +2027,7 @@ private:
     
     std::unique_ptr<UserGestureIndicator> m_temporaryUserGesture;
 
-    HashMap<String, std::unique_ptr<CSSRegisteredCustomProperty>> m_CSSRegisteredPropertySet;
+    CSSRegisteredCustomPropertySet m_CSSRegisteredPropertySet;
 
     bool m_isRunningUserScripts { false };
 };
index 38097d2..95501b6 100644 (file)
@@ -2242,7 +2242,7 @@ bool RenderStyle::hasReferenceFilterOnly() const
     return filterOperations.size() == 1 && filterOperations.at(0)->type() == FilterOperation::REFERENCE;
 }
 
-void RenderStyle::checkVariablesInCustomProperties()
+void RenderStyle::checkVariablesInCustomProperties(const CSSRegisteredCustomPropertySet& registeredProperties)
 {
     if (!m_rareInheritedData->customProperties->containsVariables)
         return;
@@ -2273,7 +2273,7 @@ void RenderStyle::checkVariablesInCustomProperties()
     for (auto entry : customProperties) {
         if (!entry.value->containsVariables())
             continue;
-        entry.value->resolveVariableReferences(customProperties, resolvedValues);
+        entry.value->resolveVariableReferences(customProperties, registeredProperties, resolvedValues);
     }
     
     // With all results computed, we can now mutate our table to eliminate the variables and
index a208500..dcd025f 100644 (file)
@@ -792,7 +792,7 @@ public:
     ApplePayButtonType applePayButtonType() const { return static_cast<ApplePayButtonType>(m_rareNonInheritedData->applePayButtonType); }
 #endif
 
-    void checkVariablesInCustomProperties();
+    void checkVariablesInCustomProperties(const CSSRegisteredCustomPropertySet&);
 
 // attribute setter methods