Registered custom properties should support syntax parameter for <length> and *
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 23 Oct 2018 06:10:59 +0000 (06:10 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 23 Oct 2018 06:10:59 +0000 (06:10 +0000)
https://bugs.webkit.org/show_bug.cgi?id=190039

Patch by Justin Michaud <justin_michaud@apple.com> on 2018-10-22
Reviewed by Antti Koivisto.

LayoutTests/imported/w3c:

Update WPT test results to fail in a new way.

* web-platform-tests/css/css-properties-values-api/register-property-syntax-parsing-expected.txt:
* web-platform-tests/css/css-properties-values-api/registered-properties-inheritance-expected.txt:
* web-platform-tests/css/css-properties-values-api/registered-property-cssom-expected.txt:
* web-platform-tests/css/css-properties-values-api/typedom.tentative-expected.txt:
* web-platform-tests/css/css-properties-values-api/var-reference-registered-properties-cycles-expected.txt:
* web-platform-tests/css/css-properties-values-api/var-reference-registered-properties-expected.txt:

Source/WebCore:

Refactor code so that:
- All properties applied in StyleResolver::applyMatchedProperties are only applied once.
- Custom properties are only resolved once, in StyleResolver, when they are applied to the RenderStyle. They were previously resolved
  every time they were referenced, and again in RenderStyle.
- The font-size property is applied after its variable references, but before custom properties that depend on it.
- Cycles are detected at the same time as resolution.
- MutableStyleProperties' custom properties cannot be set from Javascript or WebKitLegacy if they do not parse for the property's type.
  If they contain var(--...) references, however, then they can be set because we cannot check if the references are valid from setProperty.
  This behaviour matches chrome, but is not documented in the spec.
- Custom property values have more explicit resolved/unresolved state.
- RenderStyle only ever holds resolved custom properties, and StyleResolver::CascadedProperties only holds unresolved properties.

Tests: css-custom-properties-api/crash.html
       css-custom-properties-api/cycles.html
       css-custom-properties-api/inline.html

* css/CSSComputedStyleDeclaration.cpp:
(WebCore::ComputedStyleExtractor::customPropertyValue):
* css/CSSCustomPropertyValue.cpp:
(WebCore::CSSCustomPropertyValue::equals const):
(WebCore::CSSCustomPropertyValue::customCSSText const):
(WebCore::CSSCustomPropertyValue::tokens const):
(WebCore::CSSCustomPropertyValue::checkVariablesForCycles const): Deleted.
(WebCore::CSSCustomPropertyValue::resolveVariableReferences const): Deleted.
(WebCore::CSSCustomPropertyValue::setResolvedTypedValue): Deleted.
* css/CSSCustomPropertyValue.h:
* css/CSSRegisteredCustomProperty.cpp:
(WebCore::CSSRegisteredCustomProperty::CSSRegisteredCustomProperty):
* css/CSSRegisteredCustomProperty.h:
* css/CSSStyleSheet.h:
* css/CSSVariableData.cpp:
(WebCore::CSSVariableData::CSSVariableData):
(WebCore::CSSVariableData::consumeAndUpdateTokens): Deleted.
(WebCore::CSSVariableData::checkVariablesForCycles const): Deleted.
(WebCore::CSSVariableData::checkVariablesForCyclesWithRange const): Deleted.
(WebCore::CSSVariableData::resolveVariableFallback const): Deleted.
(WebCore::CSSVariableData::resolveVariableReference const): Deleted.
(WebCore::CSSVariableData::resolveVariableReferences const): Deleted.
(WebCore::CSSVariableData::resolveTokenRange const): Deleted.
* css/CSSVariableData.h:
(WebCore::CSSVariableData::create):
(WebCore::CSSVariableData::createResolved): Deleted.
(WebCore::CSSVariableData::needsVariableResolution const): Deleted.
(WebCore::CSSVariableData::CSSVariableData): Deleted.
* css/CSSVariableReferenceValue.cpp:
(WebCore::resolveVariableFallback):
(WebCore::resolveVariableReference):
(WebCore::resolveTokenRange):
(WebCore::CSSVariableReferenceValue::resolveVariableReferences const):
(WebCore::CSSVariableReferenceValue::checkVariablesForCycles const): Deleted.
* css/CSSVariableReferenceValue.h:
(WebCore::CSSVariableReferenceValue::create):
(WebCore::CSSVariableReferenceValue::equals const):
(WebCore::CSSVariableReferenceValue::variableDataValue const): Deleted.
* css/DOMCSSRegisterCustomProperty.cpp:
(WebCore::DOMCSSRegisterCustomProperty::registerProperty):
* css/PropertySetCSSStyleDeclaration.cpp:
(WebCore::PropertySetCSSStyleDeclaration::setProperty):
* css/StyleBuilderCustom.h:
(WebCore::StyleBuilderCustom::applyInitialCustomProperty):
(WebCore::StyleBuilderCustom::applyValueCustomProperty):
* css/StyleProperties.cpp:
(WebCore::MutableStyleProperties::setCustomProperty):
* css/StyleProperties.h:
* css/StyleResolver.cpp:
(WebCore::StyleResolver::State::setStyle):
(WebCore::StyleResolver::styleForKeyframe):
(WebCore::StyleResolver::styleForPage):
(WebCore::StyleResolver::applyMatchedProperties):
(WebCore::StyleResolver::applyPropertyToCurrentStyle):
(WebCore::StyleResolver::applyProperty):
(WebCore::StyleResolver::resolvedVariableValue const):
(WebCore::StyleResolver::CascadedProperties::applyDeferredProperties):
(WebCore::StyleResolver::CascadedProperties::Property::apply):
(WebCore::StyleResolver::applyCascadedCustomProperty):
(WebCore::StyleResolver::applyCascadedProperties):
* css/StyleResolver.h:
* css/parser/CSSParser.cpp:
(WebCore::CSSParser::parseValueWithVariableReferences):
* css/parser/CSSParser.h:
* css/parser/CSSPropertyParser.cpp:
(WebCore::CSSPropertyParser::CSSPropertyParser):
(WebCore::CSSPropertyParser::canParseTypedCustomPropertyValue):
(WebCore::CSSPropertyParser::parseTypedCustomPropertyValue):
(WebCore::CSSPropertyParser::collectParsedCustomPropertyValueDependencies):
(WebCore::CSSPropertyParser::parseValueStart):
(WebCore::CSSPropertyParser::parseSingleValue):
* css/parser/CSSPropertyParser.h:
* css/parser/CSSVariableParser.cpp:
(WebCore::CSSVariableParser::parseDeclarationValue):
* dom/ConstantPropertyMap.cpp:
(WebCore::ConstantPropertyMap::setValueForProperty):
(WebCore::variableDataForPositivePixelLength):
(WebCore::variableDataForPositiveDuration):
* rendering/style/RenderStyle.cpp:
(WebCore::RenderStyle::checkVariablesInCustomProperties): Deleted.
* rendering/style/RenderStyle.h:
(WebCore::RenderStyle::setInheritedCustomPropertyValue):
(WebCore::RenderStyle::setNonInheritedCustomPropertyValue):
* rendering/style/StyleCustomPropertyData.h:
(WebCore::StyleCustomPropertyData::operator== const):
(WebCore::StyleCustomPropertyData::setCustomPropertyValue):
(WebCore::StyleCustomPropertyData::StyleCustomPropertyData):
(): Deleted.

LayoutTests:

Add tests for inline styles, font-size cycles with custom properties, and a crash that was reported.

* css-custom-properties-api/crash-expected.txt: Added.
* css-custom-properties-api/crash.html: Added.
* css-custom-properties-api/cycles-expected.txt: Added.
* css-custom-properties-api/cycles.html: Added.
* css-custom-properties-api/inline-expected.txt: Added.
* css-custom-properties-api/inline.html: Added.

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

40 files changed:
LayoutTests/ChangeLog
LayoutTests/css-custom-properties-api/crash-expected.txt [new file with mode: 0644]
LayoutTests/css-custom-properties-api/crash.html [new file with mode: 0644]
LayoutTests/css-custom-properties-api/cycles-expected.txt [new file with mode: 0644]
LayoutTests/css-custom-properties-api/cycles.html [new file with mode: 0644]
LayoutTests/css-custom-properties-api/inline-expected.txt [new file with mode: 0644]
LayoutTests/css-custom-properties-api/inline.html [new file with mode: 0644]
LayoutTests/imported/w3c/ChangeLog
LayoutTests/imported/w3c/web-platform-tests/css/css-properties-values-api/register-property-syntax-parsing-expected.txt
LayoutTests/imported/w3c/web-platform-tests/css/css-properties-values-api/registered-properties-inheritance-expected.txt
LayoutTests/imported/w3c/web-platform-tests/css/css-properties-values-api/registered-property-cssom-expected.txt
LayoutTests/imported/w3c/web-platform-tests/css/css-properties-values-api/typedom.tentative-expected.txt
LayoutTests/imported/w3c/web-platform-tests/css/css-properties-values-api/var-reference-registered-properties-cycles-expected.txt
LayoutTests/imported/w3c/web-platform-tests/css/css-properties-values-api/var-reference-registered-properties-expected.txt
Source/WebCore/ChangeLog
Source/WebCore/css/CSSComputedStyleDeclaration.cpp
Source/WebCore/css/CSSCustomPropertyValue.cpp
Source/WebCore/css/CSSCustomPropertyValue.h
Source/WebCore/css/CSSRegisteredCustomProperty.cpp
Source/WebCore/css/CSSRegisteredCustomProperty.h
Source/WebCore/css/CSSVariableData.cpp
Source/WebCore/css/CSSVariableData.h
Source/WebCore/css/CSSVariableReferenceValue.cpp
Source/WebCore/css/CSSVariableReferenceValue.h
Source/WebCore/css/DOMCSSRegisterCustomProperty.cpp
Source/WebCore/css/PropertySetCSSStyleDeclaration.cpp
Source/WebCore/css/StyleBuilderCustom.h
Source/WebCore/css/StyleProperties.cpp
Source/WebCore/css/StyleProperties.h
Source/WebCore/css/StyleResolver.cpp
Source/WebCore/css/StyleResolver.h
Source/WebCore/css/parser/CSSParser.cpp
Source/WebCore/css/parser/CSSParser.h
Source/WebCore/css/parser/CSSPropertyParser.cpp
Source/WebCore/css/parser/CSSPropertyParser.h
Source/WebCore/css/parser/CSSVariableParser.cpp
Source/WebCore/dom/ConstantPropertyMap.cpp
Source/WebCore/rendering/style/RenderStyle.cpp
Source/WebCore/rendering/style/RenderStyle.h
Source/WebCore/rendering/style/StyleCustomPropertyData.h

index b762e04..7d4eb01 100644 (file)
@@ -1,3 +1,19 @@
+2018-10-22  Justin Michaud  <justin_michaud@apple.com>
+
+        Registered custom properties should support syntax parameter for <length> and *
+        https://bugs.webkit.org/show_bug.cgi?id=190039
+
+        Reviewed by Antti Koivisto.
+
+        Add tests for inline styles, font-size cycles with custom properties, and a crash that was reported.
+
+        * css-custom-properties-api/crash-expected.txt: Added.
+        * css-custom-properties-api/crash.html: Added.
+        * css-custom-properties-api/cycles-expected.txt: Added.
+        * css-custom-properties-api/cycles.html: Added.
+        * css-custom-properties-api/inline-expected.txt: Added.
+        * css-custom-properties-api/inline.html: Added.
+
 2018-10-22  Ryan Haddad  <ryanhaddad@apple.com>
 
         REGRESSION: [iOS] Layout Test media/media-fullscreen-pause-inline.html is a flaky failure
diff --git a/LayoutTests/css-custom-properties-api/crash-expected.txt b/LayoutTests/css-custom-properties-api/crash-expected.txt
new file mode 100644 (file)
index 0000000..8af0ff5
--- /dev/null
@@ -0,0 +1,8 @@
+This is text
+
+
+PASS Fallback is handled correctly, and we don't crash 
+PASS Setting the inline style is handled correctly 
+PASS CSS.registerProperty 
+PASS Setting the inline style is handled correctly when registered 
+
diff --git a/LayoutTests/css-custom-properties-api/crash.html b/LayoutTests/css-custom-properties-api/crash.html
new file mode 100644 (file)
index 0000000..7f2cd70
--- /dev/null
@@ -0,0 +1,63 @@
+<!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>
+  #test1 {
+    font-size: var(--foo, 30px);
+    --foo: var(--bar, 20px);
+    --bar: var(--baz, var(--foo));
+  }
+</style>
+<div>
+  <h2 id=test1>This is text</h2>
+</div>
+<script>
+var computedStyle = getComputedStyle(test1);
+var inlineStyle = test1.style;
+
+test(function() {
+  assert_equals(computedStyle.getPropertyValue('--baz'), '');
+  assert_equals(computedStyle.getPropertyValue('--foo'), '');
+  assert_equals(computedStyle.getPropertyValue('--bar'), '');
+  assert_equals(computedStyle.getPropertyValue('font-size'), '30px');
+}, "Fallback is handled correctly, and we don't crash");
+
+test(function() {
+  inlineStyle.setProperty('--baz', '   40px');
+  assert_equals(computedStyle.getPropertyValue('--baz'), ' 40px');
+  assert_equals(computedStyle.getPropertyValue('--foo'), '');
+  assert_equals(computedStyle.getPropertyValue('--bar'), '');
+  assert_equals(computedStyle.getPropertyValue('--baz'), ' 40px');
+  assert_equals(computedStyle.getPropertyValue('font-size'), '30px');
+  inlineStyle.removeProperty('--baz');
+  assert_equals(computedStyle.getPropertyValue('--baz'), '');
+  assert_equals(computedStyle.getPropertyValue('--foo'), '');
+  assert_equals(computedStyle.getPropertyValue('--bar'), '');
+  assert_equals(computedStyle.getPropertyValue('font-size'), '30px');
+  assert_equals(computedStyle.getPropertyValue('--baz'), '');
+}, "Setting the inline style is handled correctly");
+
+test(function() {
+  CSS.registerProperty({name: '--foo', syntax: '<length>', initialValue: '200px', inherits: true});
+  CSS.registerProperty({name: '--bar', syntax: '<length>', initialValue: '200px', inherits: true});
+  CSS.registerProperty({name: '--baz', syntax: '<length>', initialValue: '200px', inherits: true});
+}, "CSS.registerProperty");
+
+test(function() {
+  inlineStyle.setProperty('--baz', '   40px');
+  assert_equals(computedStyle.getPropertyValue('--baz'), '40px');
+  assert_equals(computedStyle.getPropertyValue('--foo'), '200px');
+  assert_equals(computedStyle.getPropertyValue('--bar'), '200px');
+  assert_equals(computedStyle.getPropertyValue('--baz'), '40px');
+  assert_equals(computedStyle.getPropertyValue('font-size'), '200px');
+  inlineStyle.removeProperty('--baz');
+  assert_equals(computedStyle.getPropertyValue('--baz'), '200px');
+  assert_equals(computedStyle.getPropertyValue('--foo'), '200px');
+  assert_equals(computedStyle.getPropertyValue('--bar'), '200px');
+  assert_equals(computedStyle.getPropertyValue('font-size'), '200px');
+  assert_equals(computedStyle.getPropertyValue('--baz'), '200px');
+}, "Setting the inline style is handled correctly when registered");
+
+</script>
diff --git a/LayoutTests/css-custom-properties-api/cycles-expected.txt b/LayoutTests/css-custom-properties-api/cycles-expected.txt
new file mode 100644 (file)
index 0000000..7d486eb
--- /dev/null
@@ -0,0 +1,18 @@
+test
+
+test
+
+test
+
+test
+
+test
+
+
+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 
+PASS JS Attributes are valid for element 5 
+
diff --git a/LayoutTests/css-custom-properties-api/cycles.html b/LayoutTests/css-custom-properties-api/cycles.html
new file mode 100644 (file)
index 0000000..6ec24c8
--- /dev/null
@@ -0,0 +1,130 @@
+<!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;
+    font-size: 10px;
+    --a: 10em;
+    width: var(--a);
+  }
+
+  #parent2 {
+    width: 500px;
+    background: blue;
+    --a: 10em;
+    font-size: calc( var(--a) + -150px);
+  }
+  #child2 {
+    background: green;
+    width: var(--a);
+  }
+
+  #parent3 {
+    width: 500px;
+    background: blue;
+    --b: 10em;
+    font-size: calc( var(--b) + -150px);
+  }
+  #child3 {
+    background: green;
+    width: var(--b);
+  }
+
+  #parent4 {
+    width: 500px;
+    background: blue;
+    --b: 10em;
+    font-size: calc( var(--b) + -150px);
+  }
+  #child4 {
+    background: green;
+    --a: var(--b);
+    font-size: var(--a);
+    width: var(--a);
+  }
+
+  #parent5 {
+    width: 500px;
+    background: blue;
+    --b: 5em;
+    font-size: calc( var(--b) + -70px);
+  }
+  #child5 {
+    background: green;
+    --c: var(--b);
+    font-size: var(--c);
+    width: var(--c);
+  }
+</style>
+<div>
+  <div id="parent1"><div id="child1"><p>test</p></div></div>
+  <div id="parent2"><div id="child2"><p>test</p></div></div>
+  <div id="parent3"><div id="child3"><p>test</p></div></div>
+  <div id="parent4"><div id="child4"><p>test</p></div></div>
+  <div id="parent5"><div id="child5"><p>test</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: '--a',
+    syntax: '<length>',
+    inherits: true,
+    initialValue: '200px'
+  })
+}, "Registration is successful");
+test(function() {
+  test_prop('child1', 'width', '100px');
+  test_prop('child1', '--a', '100px');
+  test_prop('child1', '--b', '');
+  test_prop('parent1', '--a', '200px');
+  test_prop('parent1', '--b', '');
+}, "JS Attributes are valid for element 1");
+test(function() {
+  test_prop('child2', 'width', '160px');
+  test_prop('child2', '--a', '160px');
+  test_prop('child2', '--b', '');
+  test_prop('child2', 'font-size', '16px');
+  test_prop('parent2', '--a', '160px');
+  test_prop('parent2', '--b', '');
+  test_prop('parent2', 'font-size', '16px');
+}, "JS Attributes are valid for element 2");
+test(function() {
+  test_prop('child3', 'width', '100px');
+  test_prop('child3', '--a', '200px');
+  test_prop('child3', '--b', '10em');
+  test_prop('child3', 'font-size', '10px');
+  test_prop('parent3', '--a', '200px');
+  test_prop('parent3', '--b', '10em');
+  test_prop('parent3', 'font-size', '10px');
+}, "JS Attributes are valid for element 3");
+test(function() {
+  test_prop('child4', 'width', '100px');
+  test_prop('child4', '--a', '100px');
+  test_prop('child4', '--b', '10em');
+  test_prop('child4', 'font-size', '10px');
+  test_prop('parent4', '--a', '200px');
+  test_prop('parent4', '--b', '10em');
+  test_prop('parent4', 'font-size', '10px');
+}, "JS Attributes are valid for element 4");
+test(function() {
+  test_prop('child5', 'width', '250px');
+  test_prop('child5', '--c', '5em');
+  test_prop('child5', '--b', '5em');
+  test_prop('child5', 'font-size', '50px');
+  test_prop('parent5', '--a', '200px');
+  test_prop('parent5', '--b', '5em');
+  test_prop('parent5', 'font-size', '10px');
+}, "JS Attributes are valid for element 5");
+</script>
diff --git a/LayoutTests/css-custom-properties-api/inline-expected.txt b/LayoutTests/css-custom-properties-api/inline-expected.txt
new file mode 100644 (file)
index 0000000..e568162
--- /dev/null
@@ -0,0 +1,15 @@
+test
+
+
+PASS CSSOM setters function as expected for unregistered properties 
+PASS CSS.registerProperty 
+PASS Formerly valid values are still readable from inline styles but are computed as the unset value 
+PASS Values not matching the registered type can't be set 
+PASS Values can be removed from inline styles 
+PASS Stylesheets can be modified by CSSOM 
+PASS Valid values can be set on inline styles 
+PASS Var values are accepted 
+PASS Var values are accepted without validation 
+PASS Var values are accepted without validation, even when it is obvious they will not parse 
+PASS Var values are accepted without validation, even when it is obvious they will not parse (typed) 
+
diff --git a/LayoutTests/css-custom-properties-api/inline.html b/LayoutTests/css-custom-properties-api/inline.html
new file mode 100644 (file)
index 0000000..51ba7fe
--- /dev/null
@@ -0,0 +1,120 @@
+<!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>
+  #test1 {
+    --a: 10em;
+    --b: 10em;
+  }
+</style>
+<div>
+  <div id=test1><p>test</p></div>
+</div>
+<script>
+
+var computedStyle = getComputedStyle(test1);
+var inlineStyle = test1.style;
+var sheetStyle = document.styleSheets[0].cssRules[0].style;
+
+test(function() {
+  // Nothing registered yet, whatever you specify works
+  assert_equals(computedStyle.getPropertyValue('--a'), ' 10em');
+  assert_equals(computedStyle.getPropertyValue('--b'), ' 10em');
+
+  inlineStyle.setProperty('--a', 'hello');
+  inlineStyle.setProperty('--b', 'bonjour');
+
+  assert_equals(inlineStyle.getPropertyValue('--a'), 'hello');
+  assert_equals(inlineStyle.getPropertyValue('--b'), 'bonjour');
+  assert_equals(computedStyle.getPropertyValue('--a'), 'hello');
+  assert_equals(computedStyle.getPropertyValue('--b'), 'bonjour');
+}, "CSSOM setters function as expected for unregistered properties");
+
+test(function() {
+  CSS.registerProperty({name: '--a', syntax: '<length>', initialValue: '0px', inherits: false});
+  CSS.registerProperty({name: '--registered', syntax: '<length>', initialValue: '0px', inherits: false});
+}, "CSS.registerProperty");
+
+test(function() {
+  assert_equals(inlineStyle.getPropertyValue('--a'), 'hello');
+  assert_equals(inlineStyle.getPropertyValue('--b'), 'bonjour');
+  assert_equals(computedStyle.getPropertyValue('--a'), '0px');
+  assert_equals(computedStyle.getPropertyValue('--b'), 'bonjour');
+}, "Formerly valid values are still readable from inline styles but are computed as the unset value");
+
+test(function() {
+  inlineStyle.setProperty('--a', 'hi');
+  inlineStyle.setProperty('--b', 'hi');
+  assert_equals(inlineStyle.getPropertyValue('--a'), 'hello');
+  assert_equals(inlineStyle.getPropertyValue('--b'), 'hi');
+}, "Values not matching the registered type can't be set");
+
+test(function() {
+  inlineStyle.removeProperty('--a');
+  inlineStyle.setProperty('--b', '');
+  assert_equals(inlineStyle.getPropertyValue('--a'), '');
+  assert_equals(inlineStyle.getPropertyValue('--b'), '');
+  assert_equals(computedStyle.getPropertyValue('--a'), '160px');
+  assert_equals(computedStyle.getPropertyValue('--b'), ' 10em');
+}, "Values can be removed from inline styles");
+
+test(function() {
+  sheetStyle.setProperty('--a', 'banana'); // Invalid, no change
+  assert_equals(computedStyle.getPropertyValue('--a'), '160px');
+  sheetStyle.setProperty('--a', '20px');
+  assert_equals(computedStyle.getPropertyValue('--a'), '20px');
+  sheetStyle.setProperty('--a', 'initial');
+  assert_equals(computedStyle.getPropertyValue('--a'), '0px');
+}, "Stylesheets can be modified by CSSOM");
+
+test(function() {
+  inlineStyle.setProperty('--a', '30em');
+  inlineStyle.setProperty('--b', '20em');
+  assert_equals(inlineStyle.getPropertyValue('--a'), '30em');
+  assert_equals(inlineStyle.getPropertyValue('--b'), '20em');
+  assert_equals(computedStyle.getPropertyValue('--a'), '480px');
+  assert_equals(computedStyle.getPropertyValue('--b'), '20em');
+  inlineStyle.setProperty('--a', 'inherit');
+  assert_equals(inlineStyle.getPropertyValue('--a'), 'inherit');
+  assert_equals(computedStyle.getPropertyValue('--a'), '0px');
+}, "Valid values can be set on inline styles");
+
+test(function() {
+  inlineStyle.setProperty('--b', ' 20px');
+  inlineStyle.setProperty('--a', 'calc(var(--b) + 10px)');
+  assert_equals(inlineStyle.getPropertyValue('--b'), ' 20px');
+  assert_equals(inlineStyle.getPropertyValue('--a'), 'calc(var(--b) + 10px)');
+  assert_equals(computedStyle.getPropertyValue('--b'), ' 20px');
+  assert_equals(computedStyle.getPropertyValue('--a'), '30px');
+}, "Var values are accepted");
+
+test(function() {
+  inlineStyle.setProperty('--b', 'hello');
+  inlineStyle.setProperty('--a', 'calc(var(--b) + 15px)');
+  assert_equals(inlineStyle.getPropertyValue('--b'), 'hello');
+  assert_equals(inlineStyle.getPropertyValue('--a'), 'calc(var(--b) + 15px)');
+  assert_equals(computedStyle.getPropertyValue('--b'), 'hello');
+  assert_equals(computedStyle.getPropertyValue('--a'), '0px');
+}, "Var values are accepted without validation");
+
+test(function() {
+  inlineStyle.setProperty('--b', 'hello');
+  inlineStyle.setProperty('--a', 'calc(var(--b) 15px)');
+  assert_equals(inlineStyle.getPropertyValue('--b'), 'hello');
+  assert_equals(inlineStyle.getPropertyValue('--a'), 'calc(var(--b) 15px)');
+  assert_equals(computedStyle.getPropertyValue('--b'), 'hello');
+  assert_equals(computedStyle.getPropertyValue('--a'), '0px');
+}, "Var values are accepted without validation, even when it is obvious they will not parse");
+
+test(function() {
+  inlineStyle.setProperty('--b', 'hello');
+  inlineStyle.setProperty('--a', 'calc(var(--registered) 15px)');
+  assert_equals(inlineStyle.getPropertyValue('--b'), 'hello');
+  assert_equals(inlineStyle.getPropertyValue('--a'), 'calc(var(--registered) 15px)');
+  assert_equals(computedStyle.getPropertyValue('--b'), 'hello');
+  assert_equals(computedStyle.getPropertyValue('--a'), '0px');
+}, "Var values are accepted without validation, even when it is obvious they will not parse (typed)");
+
+</script>
index 3b8d0d4..c77508f 100644 (file)
@@ -1,3 +1,19 @@
+2018-10-22  Justin Michaud  <justin_michaud@apple.com>
+
+        Registered custom properties should support syntax parameter for <length> and *
+        https://bugs.webkit.org/show_bug.cgi?id=190039
+
+        Reviewed by Antti Koivisto.
+
+        Update WPT test results to fail in a new way.
+
+        * web-platform-tests/css/css-properties-values-api/register-property-syntax-parsing-expected.txt:
+        * web-platform-tests/css/css-properties-values-api/registered-properties-inheritance-expected.txt:
+        * web-platform-tests/css/css-properties-values-api/registered-property-cssom-expected.txt:
+        * web-platform-tests/css/css-properties-values-api/typedom.tentative-expected.txt:
+        * web-platform-tests/css/css-properties-values-api/var-reference-registered-properties-cycles-expected.txt:
+        * web-platform-tests/css/css-properties-values-api/var-reference-registered-properties-expected.txt:
+
 2018-10-12  Jiewen Tan  <jiewen_tan@apple.com>
 
         Update web-platform-tests/resource-timing
index e9e2fe7..2ea78e2 100644 (file)
@@ -1,5 +1,5 @@
 
-FAIL syntax:'*', initialValue:'a' is valid The given initial value does not parse for the given syntax.
+PASS syntax:'*', initialValue:'a' is valid 
 FAIL syntax:' * ', initialValue:'b' is valid The given initial value does not parse for the given syntax.
 PASS syntax:'<length>', initialValue:'2px' is valid 
 FAIL syntax:' <number>', initialValue:'5' is valid The given initial value does not parse for the given syntax.
@@ -9,11 +9,11 @@ FAIL syntax:' <length>+ | <percentage>', initialValue:'2px 8px' is valid The giv
 PASS syntax:'<length>|<percentage>|<length-percentage>', initialValue:'2px' is valid 
 FAIL syntax:'<color> | <image> | <url> | <integer> | <angle>', initialValue:'red' is valid The given initial value does not parse for the given syntax.
 FAIL syntax:'<time> | <resolution> | <transform-list> | <custom-ident>', initialValue:'red' is valid The given initial value does not parse for the given syntax.
-FAIL syntax:'*', initialValue:':> hello' is valid The given initial value does not parse for the given syntax.
-FAIL syntax:'*', initialValue:'([ brackets ]) { yay (??)}' is valid The given initial value does not parse for the given syntax.
-FAIL syntax:'*', initialValue:'yep 'this is valid too'' is valid The given initial value does not parse for the given syntax.
-FAIL syntax:'*', initialValue:'unmatched opening bracket is valid :(' is valid The given initial value does not parse for the given syntax.
-FAIL syntax:'*', initialValue:'"' is valid The given initial value does not parse for the given syntax.
+PASS syntax:'*', initialValue:':> hello' is valid 
+PASS syntax:'*', initialValue:'([ brackets ]) { yay (??)}' is valid 
+PASS syntax:'*', initialValue:'yep 'this is valid too'' is valid 
+PASS syntax:'*', initialValue:'unmatched opening bracket is valid :(' is valid 
+PASS syntax:'*', initialValue:'"' is valid 
 PASS syntax:'<length>', initialValue:'0' is valid 
 PASS syntax:'<length>', initialValue:'10px /*:)*/' is valid 
 PASS syntax:'<length>', initialValue:' calc(-2px)' is valid 
@@ -63,7 +63,7 @@ banana\r
 FAIL syntax:'ba\f
 |      na\r|nya', initialValue:'nya' is valid The given initial value does not parse for the given syntax.
 FAIL syntax:'null', initialValue:'null' is valid The given initial value does not parse for the given syntax.
-FAIL syntax:'undefined', initialValue:'undefined' is valid The given initial value does not parse for the given syntax.
+PASS syntax:'undefined', initialValue:'undefined' is valid 
 FAIL syntax:'array', initialValue:'array' is valid The given initial value does not parse for the given syntax.
 PASS syntax:'banana,nya', initialValue:'banana' is invalid 
 PASS syntax:'banan\61', initialValue:'banana' is invalid 
@@ -85,36 +85,36 @@ PASS syntax:'unset', initialValue:'unset' is invalid
 FAIL syntax:'<length>|initial', initialValue:'10px' is invalid assert_throws: function "() => CSS.registerProperty({name: name, syntax: syntax, initialValue: initialValue, inherits: false})" did not throw
 FAIL syntax:'<length>|INHERIT', initialValue:'10px' is invalid assert_throws: function "() => CSS.registerProperty({name: name, syntax: syntax, initialValue: initialValue, inherits: false})" did not throw
 FAIL syntax:'<percentage>|unsEt', initialValue:'2%' is invalid assert_throws: function "() => CSS.registerProperty({name: name, syntax: syntax, initialValue: initialValue, inherits: false})" did not throw
-PASS syntax:'*', initialValue:'initial' is invalid 
-PASS syntax:'*', initialValue:'inherit' is invalid 
-PASS syntax:'*', initialValue:'unset' is invalid 
-PASS syntax:'*', initialValue:'revert' is invalid 
+FAIL syntax:'*', initialValue:'initial' is invalid assert_throws: function "() => CSS.registerProperty({name: name, syntax: syntax, initialValue: initialValue, inherits: false})" did not throw
+FAIL syntax:'*', initialValue:'inherit' is invalid assert_throws: function "() => CSS.registerProperty({name: name, syntax: syntax, initialValue: initialValue, inherits: false})" did not throw
+FAIL syntax:'*', initialValue:'unset' is invalid assert_throws: function "() => CSS.registerProperty({name: name, syntax: syntax, initialValue: initialValue, inherits: false})" did not throw
+FAIL syntax:'*', initialValue:'revert' is invalid assert_throws: function "() => CSS.registerProperty({name: name, syntax: syntax, initialValue: initialValue, inherits: false})" did not throw
 PASS syntax:'<custom-ident>', initialValue:'initial' is invalid 
 PASS syntax:'<custom-ident>+', initialValue:'foo inherit bar' is invalid 
-PASS syntax:'*', initialValue:')' is invalid 
-PASS syntax:'*', initialValue:'([)]' is invalid 
-PASS syntax:'*', initialValue:'whee!' is invalid 
-PASS syntax:'*', initialValue:'"
-' is invalid 
-PASS syntax:'*', initialValue:'url(moo '')' is invalid 
-PASS syntax:'*', initialValue:'semi;colon' is invalid 
-PASS syntax:'*', initialValue:'var(invalid var ref)' is invalid 
-PASS syntax:'*', initialValue:'var(--foo)' is invalid 
+FAIL syntax:'*', initialValue:')' is invalid assert_throws: function "() => CSS.registerProperty({name: name, syntax: syntax, initialValue: initialValue, inherits: false})" did not throw
+FAIL syntax:'*', initialValue:'([)]' is invalid assert_throws: function "() => CSS.registerProperty({name: name, syntax: syntax, initialValue: initialValue, inherits: false})" did not throw
+FAIL syntax:'*', initialValue:'whee!' is invalid assert_throws: function "() => CSS.registerProperty({name: name, syntax: syntax, initialValue: initialValue, inherits: false})" did not throw
+FAIL syntax:'*', initialValue:'"
+' is invalid assert_throws: function "() => CSS.registerProperty({name: name, syntax: syntax, initialValue: initialValue, inherits: false})" did not throw
+FAIL syntax:'*', initialValue:'url(moo '')' is invalid assert_throws: function "() => CSS.registerProperty({name: name, syntax: syntax, initialValue: initialValue, inherits: false})" did not throw
+FAIL syntax:'*', initialValue:'semi;colon' is invalid assert_throws: function "() => CSS.registerProperty({name: name, syntax: syntax, initialValue: initialValue, inherits: false})" did not throw
+FAIL syntax:'*', initialValue:'var(invalid var ref)' is invalid assert_throws: function "() => CSS.registerProperty({name: name, syntax: syntax, initialValue: initialValue, inherits: false})" did not throw
+FAIL syntax:'*', initialValue:'var(--foo)' is invalid assert_throws: function "() => CSS.registerProperty({name: name, syntax: syntax, initialValue: initialValue, inherits: false})" did not throw
 PASS syntax:'banana', initialValue:'bAnAnA' is invalid 
 PASS syntax:'<length>', initialValue:'var(--moo)' is invalid 
 PASS syntax:'<length>', initialValue:'10' is invalid 
 FAIL syntax:'<length>', initialValue:'10%' is invalid assert_throws: function "() => CSS.registerProperty({name: name, syntax: syntax, initialValue: initialValue, inherits: false})" did not throw
 FAIL syntax:'<length>', initialValue:'calc(5px + 10%)' is invalid assert_throws: function "() => CSS.registerProperty({name: name, syntax: syntax, initialValue: initialValue, inherits: false})" did not throw
 PASS syntax:'<length>', initialValue:'calc(5px * 3px / 6px)' is invalid 
-PASS syntax:'<length>', initialValue:'10em' is invalid 
+FAIL syntax:'<length>', initialValue:'10em' is invalid assert_throws: function "() => CSS.registerProperty({name: name, syntax: syntax, initialValue: initialValue, inherits: false})" did not throw
 FAIL syntax:'<length>', initialValue:'10vmin' is invalid assert_throws: function "() => CSS.registerProperty({name: name, syntax: syntax, initialValue: initialValue, inherits: false})" did not throw
-PASS syntax:'<length>', initialValue:'calc(4px + 3em)' is invalid 
-PASS syntax:'<length>', initialValue:'calc(4px + calc(8 * 2em))' is invalid 
-PASS syntax:'<length>+', initialValue:'calc(2ex + 16px)' is invalid 
+FAIL syntax:'<length>', initialValue:'calc(4px + 3em)' is invalid assert_throws: function "() => CSS.registerProperty({name: name, syntax: syntax, initialValue: initialValue, inherits: false})" did not throw
+FAIL syntax:'<length>', initialValue:'calc(4px + calc(8 * 2em))' is invalid assert_throws: function "() => CSS.registerProperty({name: name, syntax: syntax, initialValue: initialValue, inherits: false})" did not throw
+FAIL syntax:'<length>+', initialValue:'calc(2ex + 16px)' is invalid assert_throws: function "() => CSS.registerProperty({name: name, syntax: syntax, initialValue: initialValue, inherits: false})" did not throw
 PASS syntax:'<length>+', initialValue:'10px calc(20px + 4rem)' is invalid 
 PASS syntax:'<percentage> | <length>+', initialValue:'calc(100vh - 10px) 30px' is invalid 
 PASS syntax:'<length>', initialValue:'10px;' is invalid 
-PASS syntax:'<length-percentage>', initialValue:'calc(2px + 10% + 7ex)' is invalid 
+FAIL syntax:'<length-percentage>', initialValue:'calc(2px + 10% + 7ex)' is invalid assert_throws: function "() => CSS.registerProperty({name: name, syntax: syntax, initialValue: initialValue, inherits: false})" did not throw
 FAIL syntax:'<percentage>', initialValue:'0' is invalid assert_throws: function "() => CSS.registerProperty({name: name, syntax: syntax, initialValue: initialValue, inherits: false})" did not throw
 PASS syntax:'<integer>', initialValue:'1.0' is invalid 
 PASS syntax:'<integer>', initialValue:'1e0' is invalid 
index fc27b0d..5654e82 100644 (file)
@@ -2,6 +2,6 @@
 PASS Registered properties are correctly inherited (or not) depending on the inherits flag. 
 PASS Explicitly inheriting from a parent with an invalid value results in initial value. 
 PASS Explicitly inheriting from a parent with no value results in initial value. 
-FAIL Reference to undefined variable results in inherited value assert_equals: expected "42px" but got "0px"
-FAIL Reference to syntax-incompatible variable results in inherited value assert_equals: expected "42px" but got "0px"
+PASS Reference to undefined variable results in inherited value 
+PASS Reference to syntax-incompatible variable results in inherited value 
 
index 0d5561f..efcc5c2 100644 (file)
@@ -2,8 +2,8 @@
 PASS CSSOM setters function as expected for unregistered properties 
 FAIL CSS.registerProperty The given initial value does not parse for the given syntax.
 FAIL Formerly valid values are still readable from inline styles but are computed as the unset value assert_equals: expected "blue" but got "hello"
-FAIL Values not matching the registered type can't be set assert_equals: expected "5" but got "hi"
+FAIL Values not matching the registered type can't be set assert_equals: expected "hello" but got "20"
 FAIL Values can be removed from inline styles assert_equals: expected "red" but got " red"
-FAIL Stylesheets can be modified by CSSOM assert_equals: expected "10px" but got "0px"
+PASS Stylesheets can be modified by CSSOM 
 FAIL Valid values can be set on inline styles assert_equals: expected "blue" but got " blue"
 
index 1e4d270..66fbbca 100644 (file)
@@ -2,7 +2,7 @@ CONSOLE MESSAGE: line 340: ReferenceError: Can't find variable: CSSUnparsedValue
 
 Harness Error (FAIL), message = ReferenceError: Can't find variable: CSSUnparsedValue
 
-FAIL Computed * is reified as CSSUnparsedValue The given initial value does not parse for the given syntax.
+FAIL Computed * is reified as CSSUnparsedValue target.computedStyleMap is not a function. (In 'target.computedStyleMap()', 'target.computedStyleMap' is undefined)
 FAIL Computed <angle> is reified as CSSUnitValue The given initial value does not parse for the given syntax.
 FAIL Computed <color> is reified as CSSStyleValue The given initial value does not parse for the given syntax.
 FAIL Computed <custom-ident> is reified as CSSKeywordValue The given initial value does not parse for the given syntax.
index 2bedf68..9314512 100644 (file)
@@ -1,6 +1,6 @@
 
-FAIL A var() cycle between two registered properties is handled correctly. assert_equals: expected "2px" but got "3px"
-FAIL A var() cycle between a registered properties and an unregistered property is handled correctly. assert_equals: expected "1px" but got "2px"
+PASS A var() cycle between two registered properties is handled correctly. 
+PASS A var() cycle between a registered properties and an unregistered property is handled correctly. 
 PASS A var() cycle between a two unregistered properties is handled correctly. 
-FAIL A var() cycle between a syntax:'*' property and an unregistered property is handled correctly. The given initial value does not parse for the given syntax.
+PASS A var() cycle between a syntax:'*' property and an unregistered property is handled correctly. 
 
index db535e4..39e675d 100644 (file)
@@ -1,5 +1,5 @@
 
-FAIL var() references work with registered properties assert_equals: expected "15px" but got "10%"
+FAIL var() references work with registered properties assert_equals: expected "  10px" but got " 10px"
 FAIL References to registered var()-properties work in registered lists assert_equals: expected "1px, 10px, 2px" but got "0px"
 FAIL References to mixed registered and unregistered var()-properties work in registered lists assert_equals: expected "1px, 20px, 10px, 2px" but got "0px"
 FAIL Registered lists may be concatenated assert_equals: expected "1px, 10px, 2px, 1px, 20px, 10px, 2px" but got "0px"
index da887e5..ee23f29 100644 (file)
@@ -1,5 +1,117 @@
 2018-10-22  Justin Michaud  <justin_michaud@apple.com>
 
+        Registered custom properties should support syntax parameter for <length> and *
+        https://bugs.webkit.org/show_bug.cgi?id=190039
+
+        Reviewed by Antti Koivisto.
+
+        Refactor code so that:
+        - All properties applied in StyleResolver::applyMatchedProperties are only applied once. 
+        - Custom properties are only resolved once, in StyleResolver, when they are applied to the RenderStyle. They were previously resolved
+          every time they were referenced, and again in RenderStyle.
+        - The font-size property is applied after its variable references, but before custom properties that depend on it.
+        - Cycles are detected at the same time as resolution.
+        - MutableStyleProperties' custom properties cannot be set from Javascript or WebKitLegacy if they do not parse for the property's type.
+          If they contain var(--...) references, however, then they can be set because we cannot check if the references are valid from setProperty.
+          This behaviour matches chrome, but is not documented in the spec. 
+        - Custom property values have more explicit resolved/unresolved state.
+        - RenderStyle only ever holds resolved custom properties, and StyleResolver::CascadedProperties only holds unresolved properties.
+
+        Tests: css-custom-properties-api/crash.html
+               css-custom-properties-api/cycles.html
+               css-custom-properties-api/inline.html
+
+        * css/CSSComputedStyleDeclaration.cpp:
+        (WebCore::ComputedStyleExtractor::customPropertyValue):
+        * css/CSSCustomPropertyValue.cpp:
+        (WebCore::CSSCustomPropertyValue::equals const):
+        (WebCore::CSSCustomPropertyValue::customCSSText const):
+        (WebCore::CSSCustomPropertyValue::tokens const):
+        (WebCore::CSSCustomPropertyValue::checkVariablesForCycles const): Deleted.
+        (WebCore::CSSCustomPropertyValue::resolveVariableReferences const): Deleted.
+        (WebCore::CSSCustomPropertyValue::setResolvedTypedValue): Deleted.
+        * css/CSSCustomPropertyValue.h:
+        * css/CSSRegisteredCustomProperty.cpp:
+        (WebCore::CSSRegisteredCustomProperty::CSSRegisteredCustomProperty):
+        * css/CSSRegisteredCustomProperty.h:
+        * css/CSSStyleSheet.h:
+        * css/CSSVariableData.cpp:
+        (WebCore::CSSVariableData::CSSVariableData):
+        (WebCore::CSSVariableData::consumeAndUpdateTokens): Deleted.
+        (WebCore::CSSVariableData::checkVariablesForCycles const): Deleted.
+        (WebCore::CSSVariableData::checkVariablesForCyclesWithRange const): Deleted.
+        (WebCore::CSSVariableData::resolveVariableFallback const): Deleted.
+        (WebCore::CSSVariableData::resolveVariableReference const): Deleted.
+        (WebCore::CSSVariableData::resolveVariableReferences const): Deleted.
+        (WebCore::CSSVariableData::resolveTokenRange const): Deleted.
+        * css/CSSVariableData.h:
+        (WebCore::CSSVariableData::create):
+        (WebCore::CSSVariableData::createResolved): Deleted.
+        (WebCore::CSSVariableData::needsVariableResolution const): Deleted.
+        (WebCore::CSSVariableData::CSSVariableData): Deleted.
+        * css/CSSVariableReferenceValue.cpp:
+        (WebCore::resolveVariableFallback):
+        (WebCore::resolveVariableReference):
+        (WebCore::resolveTokenRange):
+        (WebCore::CSSVariableReferenceValue::resolveVariableReferences const):
+        (WebCore::CSSVariableReferenceValue::checkVariablesForCycles const): Deleted.
+        * css/CSSVariableReferenceValue.h:
+        (WebCore::CSSVariableReferenceValue::create):
+        (WebCore::CSSVariableReferenceValue::equals const):
+        (WebCore::CSSVariableReferenceValue::variableDataValue const): Deleted.
+        * css/DOMCSSRegisterCustomProperty.cpp:
+        (WebCore::DOMCSSRegisterCustomProperty::registerProperty):
+        * css/PropertySetCSSStyleDeclaration.cpp:
+        (WebCore::PropertySetCSSStyleDeclaration::setProperty):
+        * css/StyleBuilderCustom.h:
+        (WebCore::StyleBuilderCustom::applyInitialCustomProperty):
+        (WebCore::StyleBuilderCustom::applyValueCustomProperty):
+        * css/StyleProperties.cpp:
+        (WebCore::MutableStyleProperties::setCustomProperty):
+        * css/StyleProperties.h:
+        * css/StyleResolver.cpp:
+        (WebCore::StyleResolver::State::setStyle):
+        (WebCore::StyleResolver::styleForKeyframe):
+        (WebCore::StyleResolver::styleForPage):
+        (WebCore::StyleResolver::applyMatchedProperties):
+        (WebCore::StyleResolver::applyPropertyToCurrentStyle):
+        (WebCore::StyleResolver::applyProperty):
+        (WebCore::StyleResolver::resolvedVariableValue const):
+        (WebCore::StyleResolver::CascadedProperties::applyDeferredProperties):
+        (WebCore::StyleResolver::CascadedProperties::Property::apply):
+        (WebCore::StyleResolver::applyCascadedCustomProperty):
+        (WebCore::StyleResolver::applyCascadedProperties):
+        * css/StyleResolver.h:
+        * css/parser/CSSParser.cpp:
+        (WebCore::CSSParser::parseValueWithVariableReferences):
+        * css/parser/CSSParser.h:
+        * css/parser/CSSPropertyParser.cpp:
+        (WebCore::CSSPropertyParser::CSSPropertyParser):
+        (WebCore::CSSPropertyParser::canParseTypedCustomPropertyValue):
+        (WebCore::CSSPropertyParser::parseTypedCustomPropertyValue):
+        (WebCore::CSSPropertyParser::collectParsedCustomPropertyValueDependencies):
+        (WebCore::CSSPropertyParser::parseValueStart):
+        (WebCore::CSSPropertyParser::parseSingleValue):
+        * css/parser/CSSPropertyParser.h:
+        * css/parser/CSSVariableParser.cpp:
+        (WebCore::CSSVariableParser::parseDeclarationValue):
+        * dom/ConstantPropertyMap.cpp:
+        (WebCore::ConstantPropertyMap::setValueForProperty):
+        (WebCore::variableDataForPositivePixelLength):
+        (WebCore::variableDataForPositiveDuration):
+        * rendering/style/RenderStyle.cpp:
+        (WebCore::RenderStyle::checkVariablesInCustomProperties): Deleted.
+        * rendering/style/RenderStyle.h:
+        (WebCore::RenderStyle::setInheritedCustomPropertyValue):
+        (WebCore::RenderStyle::setNonInheritedCustomPropertyValue):
+        * rendering/style/StyleCustomPropertyData.h:
+        (WebCore::StyleCustomPropertyData::operator== const):
+        (WebCore::StyleCustomPropertyData::setCustomPropertyValue):
+        (WebCore::StyleCustomPropertyData::StyleCustomPropertyData):
+        (): Deleted.
+
+2018-10-22  Justin Michaud  <justin_michaud@apple.com>
+
         CSS Paint API should give a 2d rendering context
         https://bugs.webkit.org/show_bug.cgi?id=190762
 
index c77648e..0749d9a 100644 (file)
@@ -2612,21 +2612,23 @@ RefPtr<CSSValue> ComputedStyleExtractor::customPropertyValue(const String& prope
     auto* registered = styledElement->document().getCSSRegisteredCustomPropertySet().get(propertyName);
     auto* value = style->getCustomProperty(propertyName);
 
-    if (registered) {
-        // TODO this should be done based on the syntax
-        if (value && value->resolvedTypedValue())
-            return zoomAdjustedPixelValueForLength(*value->resolvedTypedValue(), *style);
-
-        if (registered->initialValue() && registered->initialValue()->resolvedTypedValue())
-            return zoomAdjustedPixelValueForLength(*registered->initialValue()->resolvedTypedValue(), *style);
+    if (registered && !value)
+        return registered->initialValueCopy();
 
+    if (!value)
         return nullptr;
-    }
 
-    if (value)
+    auto visitor = WTF::makeVisitor([&](const Ref<CSSVariableReferenceValue>&) {
+        ASSERT_NOT_REACHED();
+        return RefPtr<CSSValue>();
+    }, [&](const CSSValueID&) {
         return CSSCustomPropertyValue::create(*value);
-
-    return nullptr;
+    }, [&](const Ref<CSSVariableData>&) {
+        return CSSCustomPropertyValue::create(*value);
+    }, [&](const Length& value) {
+        return zoomAdjustedPixelValueForLength(value, *style);
+    });
+    return WTF::visit(visitor, value->value());
 }
 
 String ComputedStyleExtractor::customPropertyText(const String& propertyName)
index 7c16e84..e6966ff 100644 (file)
 #include "CSSCustomPropertyValue.h"
 #include "CSSTokenizer.h"
 
-
 namespace WebCore {
 
+bool CSSCustomPropertyValue::equals(const CSSCustomPropertyValue& other) const
+{
+    if (m_name != other.m_name || m_value.index() != other.m_value.index())
+        return false;
+    auto visitor = WTF::makeVisitor([&](const Ref<CSSVariableReferenceValue>& value) {
+        return value.get() == WTF::get<Ref<CSSVariableReferenceValue>>(other.m_value).get();
+    }, [&](const CSSValueID& value) {
+        return value == WTF::get<CSSValueID>(other.m_value);
+    }, [&](const Ref<CSSVariableData>& value) {
+        return value.get() == WTF::get<Ref<CSSVariableData>>(other.m_value).get();
+    }, [&](const Length& value) {
+        return value == WTF::get<Length>(other.m_value);
+    });
+    return WTF::visit(visitor, m_value);
+}
+
 String CSSCustomPropertyValue::customCSSText() const
 {
     if (!m_serialized) {
         m_serialized = true;
-        if (m_resolvedTypedValue) // FIXME: Unit should be based on syntax.
-            m_stringValue = CSSPrimitiveValue::create(m_resolvedTypedValue->value(), CSSPrimitiveValue::CSS_PX)->cssText();
-        else if (m_value)
-            m_stringValue = m_value->tokenRange().serialize();
-        else if (m_valueId != CSSValueInvalid)
-            m_stringValue = getValueName(m_valueId);
-        else
-            m_stringValue = emptyString();
+
+        auto visitor = WTF::makeVisitor([&](const Ref<CSSVariableReferenceValue>& value) {
+            m_stringValue = value->cssText();
+        }, [&](const CSSValueID& value) {
+            m_stringValue = getValueName(value);
+        }, [&](const Ref<CSSVariableData>& value) {
+            m_stringValue = value->tokenRange().serialize();
+        }, [&](const Length& value) {
+            m_stringValue = CSSPrimitiveValue::create(value.value(), CSSPrimitiveValue::CSS_PX)->cssText();
+        });
+        WTF::visit(visitor, m_value);
     }
     return m_stringValue;
 }
 
-Vector<CSSParserToken> CSSCustomPropertyValue::tokens(const CSSRegisteredCustomPropertySet& registeredProperties, const RenderStyle& style) const
+Vector<CSSParserToken> CSSCustomPropertyValue::tokens() const
 {
-    if (m_resolvedTypedValue) {
-        Vector<CSSParserToken> result;
+    Vector<CSSParserToken> result;
+
+    auto visitor = WTF::makeVisitor([&](const Ref<CSSVariableReferenceValue>&) {
+        ASSERT_NOT_REACHED();
+    }, [&](const CSSValueID&) {
+        // Do nothing
+    }, [&](const Ref<CSSVariableData>& value) {
+        result.appendVector(value->tokens());
+    }, [&](const Length&) {
         CSSTokenizer tokenizer(cssText());
 
         auto tokenizerRange = tokenizer.tokenRange();
         while (!tokenizerRange.atEnd())
             result.append(tokenizerRange.consume());
+    });
+    WTF::visit(visitor, m_value);
 
-        return result;
-    }
-
-    if (!m_value)
-        return { };
-
-    if (m_containsVariables) {
-        Vector<CSSParserToken> result;
-        // FIXME: Avoid doing this work more than once.
-        RefPtr<CSSVariableData> resolvedData = m_value->resolveVariableReferences(registeredProperties, style);
-        if (resolvedData)
-            result.appendVector(resolvedData->tokens());
-
-        return result;
-    }
-
-    return m_value->tokens();
-}
-
-bool CSSCustomPropertyValue::checkVariablesForCycles(const AtomicString& name, const RenderStyle& style, HashSet<AtomicString>& seenProperties, HashSet<AtomicString>& invalidProperties) const
-{
-    ASSERT(containsVariables());
-    if (m_value)
-        return m_value->checkVariablesForCycles(name, style, seenProperties, invalidProperties);
-    return true;
-}
-
-void CSSCustomPropertyValue::resolveVariableReferences(const CSSRegisteredCustomPropertySet& registeredProperties, Vector<Ref<CSSCustomPropertyValue>>& resolvedValues, const RenderStyle& style) const
-{
-    ASSERT(containsVariables());
-    if (!m_value)
-        return;
-    
-    ASSERT(m_value->needsVariableResolution());
-    RefPtr<CSSVariableData> resolvedData = m_value->resolveVariableReferences(registeredProperties, style);
-    if (resolvedData)
-        resolvedValues.append(CSSCustomPropertyValue::createWithVariableData(m_name, resolvedData.releaseNonNull()));
-    else
-        resolvedValues.append(CSSCustomPropertyValue::createWithID(m_name, CSSValueInvalid));
-}
-
-void CSSCustomPropertyValue::setResolvedTypedValue(Length length)
-{
-    ASSERT(length.isSpecified());
-    m_resolvedTypedValue = WTFMove(length);
+    return result;
 }
 
 }
index bb09214..625e195 100644 (file)
 
 #include "CSSRegisteredCustomProperty.h"
 #include "CSSValue.h"
-#include "CSSVariableData.h"
+#include "CSSVariableReferenceValue.h"
 #include "Length.h"
 #include <wtf/RefPtr.h>
+#include <wtf/Variant.h>
 #include <wtf/text/WTFString.h>
 
 namespace WebCore {
 
 class CSSParserToken;
+class CSSVariableReferenceValue;
 class RenderStyle;
 
 class CSSCustomPropertyValue final : public CSSValue {
 public:
-    static Ref<CSSCustomPropertyValue> createWithVariableData(const AtomicString& name, Ref<CSSVariableData>&& value)
+    using VariantValue = Variant<Ref<CSSVariableReferenceValue>, CSSValueID, Ref<CSSVariableData>, Length>;
+
+    static Ref<CSSCustomPropertyValue> createUnresolved(const AtomicString& name, Ref<CSSVariableReferenceValue>&& value)
     {
-        return adoptRef(*new CSSCustomPropertyValue(name, WTFMove(value)));
+        return adoptRef(*new CSSCustomPropertyValue(name, { WTFMove(value) }));
     }
-    
-    static Ref<CSSCustomPropertyValue> createWithID(const AtomicString& name, CSSValueID value)
+
+    static Ref<CSSCustomPropertyValue> createUnresolved(const AtomicString& name, CSSValueID value)
     {
-        return adoptRef(*new CSSCustomPropertyValue(name, value));
+        return adoptRef(*new CSSCustomPropertyValue(name, { value }));
+    }
+
+    static Ref<CSSCustomPropertyValue> createWithID(const AtomicString& name, CSSValueID id)
+    {
+        ASSERT(id == CSSValueInherit || id == CSSValueInitial || id == CSSValueUnset || id == CSSValueRevert || id == CSSValueInvalid);
+        return adoptRef(*new CSSCustomPropertyValue(name, { id }));
+    }
+
+    static Ref<CSSCustomPropertyValue> createSyntaxAll(const AtomicString& name, Ref<CSSVariableData>&& value)
+    {
+        return adoptRef(*new CSSCustomPropertyValue(name, { WTFMove(value) }));
     }
     
-    static Ref<CSSCustomPropertyValue> createInvalid()
+    static Ref<CSSCustomPropertyValue> createSyntaxLength(const AtomicString& name, Length value)
     {
-        return adoptRef(*new CSSCustomPropertyValue(emptyString(), emptyString()));
+        return adoptRef(*new CSSCustomPropertyValue(name, { WTFMove(value) }));
     }
 
     static Ref<CSSCustomPropertyValue> create(const CSSCustomPropertyValue& other)
@@ -62,70 +77,49 @@ public:
     String customCSSText() const;
 
     const AtomicString& name() const { return m_name; }
-    
-    bool equals(const CSSCustomPropertyValue& other) const { return m_name == other.m_name && m_value == other.m_value && m_valueId == other.m_valueId; }
-
-    bool containsVariables() const { ASSERT(!m_containsVariables || !m_resolvedTypedValue); return m_containsVariables; }
-    bool checkVariablesForCycles(const AtomicString& name, const RenderStyle&, HashSet<AtomicString>& seenProperties, HashSet<AtomicString>& invalidProperties) const;
-
-    void resolveVariableReferences(const CSSRegisteredCustomPropertySet&, Vector<Ref<CSSCustomPropertyValue>>&, const RenderStyle&) const;
+    bool isResolved() const  { return !WTF::holds_alternative<Ref<CSSVariableReferenceValue>>(m_value); }
+    bool isUnset() const  { return WTF::holds_alternative<CSSValueID>(m_value) && WTF::get<CSSValueID>(m_value) == CSSValueUnset; }
+    bool isInvalid() const  { return WTF::holds_alternative<CSSValueID>(m_value) && WTF::get<CSSValueID>(m_value) == CSSValueInvalid; }
 
-    CSSValueID valueID() const { return m_valueId; }
-    CSSVariableData* value() const { return m_value.get(); }
-    Vector<CSSParserToken> tokens(const CSSRegisteredCustomPropertySet&, const RenderStyle&) const;
+    const VariantValue& value() const { return m_value; }
 
-    const std::optional<Length>& resolvedTypedValue() const { return m_resolvedTypedValue; }
-    void setResolvedTypedValue(Length);
+    Vector<CSSParserToken> tokens() const;
+    bool equals(const CSSCustomPropertyValue& other) const;
 
 private:
-    CSSCustomPropertyValue(const AtomicString& name, const String& serializedValue)
-        : CSSValue(CustomPropertyClass)
-        , m_name(name)
-        , m_stringValue(serializedValue)
-        , m_serialized(true)
-    {
-    }
-
-    CSSCustomPropertyValue(const AtomicString& name, CSSValueID id)
-        : CSSValue(CustomPropertyClass)
-        , m_name(name)
-        , m_valueId(id)
-    {
-        ASSERT(id == CSSValueInherit || id == CSSValueInitial || id == CSSValueUnset || id == CSSValueRevert || id == CSSValueInvalid);
-    }
-    
-    CSSCustomPropertyValue(const AtomicString& name, Ref<CSSVariableData>&& value)
+    CSSCustomPropertyValue(const AtomicString& name, VariantValue&& value)
         : CSSValue(CustomPropertyClass)
         , m_name(name)
         , m_value(WTFMove(value))
-        , m_valueId(CSSValueInternalVariableValue)
-        , m_containsVariables(m_value->needsVariableResolution())
+        , m_serialized(false)
     {
     }
 
     CSSCustomPropertyValue(const CSSCustomPropertyValue& other)
         : CSSValue(CustomPropertyClass)
         , m_name(other.m_name)
-        , m_value(other.m_value.copyRef())
-        , m_valueId(other.m_valueId)
+        , m_value(CSSValueUnset)
         , m_stringValue(other.m_stringValue)
-        , m_containsVariables(other.m_containsVariables)
         , m_serialized(other.m_serialized)
-        , m_resolvedTypedValue(other.m_resolvedTypedValue)
     {
+        // No copy constructor for Ref<CSSVariableData>, so we have to do this ourselves
+        auto visitor = WTF::makeVisitor([&](const Ref<CSSVariableReferenceValue>& value) {
+            m_value = value.copyRef();
+        }, [&](const CSSValueID& value) {
+            m_value = value;
+        }, [&](const Ref<CSSVariableData>& value) {
+            m_value = value.copyRef();
+        }, [&](const Length& value) {
+            m_value = value;
+        });
+        WTF::visit(visitor, other.m_value);
     }
     
     const AtomicString m_name;
-    
-    RefPtr<CSSVariableData> m_value;
-    CSSValueID m_valueId { CSSValueInvalid };
+    VariantValue m_value;
     
     mutable String m_stringValue;
-    bool m_containsVariables { false };
     mutable bool m_serialized { false };
-
-    // FIXME: It should not be possible to express an invalid state, such as containsVariables() && resolvedTypedValue().
-    std::optional<Length> m_resolvedTypedValue;
 };
 
 } // namespace WebCore
index 3ea7b92..79ef7fe 100644 (file)
@@ -30,8 +30,9 @@
 
 namespace WebCore {
 
-CSSRegisteredCustomProperty::CSSRegisteredCustomProperty(const String& name, bool inherits, RefPtr<CSSCustomPropertyValue>&& initialValue)
+CSSRegisteredCustomProperty::CSSRegisteredCustomProperty(const String& name, const String& syntax, bool inherits, RefPtr<CSSCustomPropertyValue>&& initialValue)
     : name(name)
+    , syntax(syntax)
     , inherits(inherits)
     , m_initialValue(WTFMove(initialValue))
 {
index 3427eaa..dfcf0bd 100644 (file)
@@ -33,10 +33,10 @@ class CSSCustomPropertyValue;
 
 struct CSSRegisteredCustomProperty {
     const String name;
-    /* TODO syntax */
+    const String syntax;
     const bool inherits;
 
-    CSSRegisteredCustomProperty(const String& name, bool inherits, RefPtr<CSSCustomPropertyValue>&& initialValue);
+    CSSRegisteredCustomProperty(const String& name, const String& syntax, bool inherits, RefPtr<CSSCustomPropertyValue>&& initialValue);
 
     const CSSCustomPropertyValue* initialValue() const { return m_initialValue.get(); }
     RefPtr<CSSCustomPropertyValue> initialValueCopy() const;
index 9899353..604ca18 100644 (file)
@@ -60,7 +60,7 @@ bool CSSVariableData::operator==(const CSSVariableData& other) const
     return tokens() == other.tokens();
 }
 
-void CSSVariableData::consumeAndUpdateTokens(const CSSParserTokenRange& range)
+CSSVariableData::CSSVariableData(const CSSParserTokenRange& range)
 {
     StringBuilder stringBuilder;
     CSSParserTokenRange localRange = range;
@@ -77,109 +77,4 @@ void CSSVariableData::consumeAndUpdateTokens(const CSSParserTokenRange& range)
         updateTokens<UChar>(range);
 }
 
-CSSVariableData::CSSVariableData(const CSSParserTokenRange& range, bool needsVariableResolution)
-    : m_needsVariableResolution(needsVariableResolution)
-{
-    consumeAndUpdateTokens(range);
-}
-
-bool CSSVariableData::checkVariablesForCycles(const AtomicString& name, const RenderStyle& style, HashSet<AtomicString>& seenProperties, HashSet<AtomicString>& invalidProperties) const
-{
-    if (invalidProperties.contains(name))
-        return false;
-    
-    HashSet<AtomicString> newSeenProperties = seenProperties;
-    newSeenProperties.add(name);
-    
-    bool valid = checkVariablesForCyclesWithRange(m_tokens, style, newSeenProperties, invalidProperties);
-    if (!valid)
-        invalidProperties.add(name);
-    
-    return valid;
-}
-    
-bool CSSVariableData::checkVariablesForCyclesWithRange(CSSParserTokenRange range, const RenderStyle& style, HashSet<AtomicString>& seenProperties, HashSet<AtomicString>& invalidProperties) const
-{
-    while (!range.atEnd()) {
-        if (range.peek().functionId() == CSSValueVar || range.peek().functionId() == CSSValueEnv) {
-            CSSParserTokenRange block = range.consumeBlock();
-            
-            block.consumeWhitespace();
-            ASSERT(block.peek().type() == IdentToken);
-            AtomicString variableName = block.consumeIncludingWhitespace().value().toAtomicString();
-            ASSERT(block.atEnd() || block.peek().type() == CommaToken);
-            if (seenProperties.contains(variableName))
-                return false;
-
-            auto* value = style.getCustomProperty(variableName);
-            if (value && value->containsVariables() && !value->checkVariablesForCycles(variableName, style, seenProperties, invalidProperties))
-                return false;
-
-            if (range.peek().type() == CommaToken) {
-                // Fallback.
-                range.consume();
-                return checkVariablesForCyclesWithRange(block, style, seenProperties, invalidProperties);
-            }
-        } else
-            range.consume();
-    }
-    return true;
-}
-
-bool CSSVariableData::resolveVariableFallback(const CSSRegisteredCustomPropertySet& registeredProperties, CSSParserTokenRange range, Vector<CSSParserToken>& result, const RenderStyle& style) const
-{
-    if (range.atEnd())
-        return false;
-    ASSERT(range.peek().type() == CommaToken);
-    range.consume();
-    return resolveTokenRange(registeredProperties, range, result, style);
-}
-
-bool CSSVariableData::resolveVariableReference(const CSSRegisteredCustomPropertySet& registeredProperties, CSSParserTokenRange range, Vector<CSSParserToken>& result, const RenderStyle& style) const
-{
-    range.consumeWhitespace();
-    ASSERT(range.peek().type() == IdentToken);
-    AtomicString variableName = range.consumeIncludingWhitespace().value().toAtomicString();
-    ASSERT(range.atEnd() || (range.peek().type() == CommaToken));
-    
-    auto* property = style.getCustomProperty(variableName);
-    if (property && property->resolvedTypedValue()) {
-        result.appendVector(property->tokens(registeredProperties, style));
-        return true;
-    }
-
-    if (!property || !property->value()) {
-        auto* registered = registeredProperties.get(variableName);
-        if (registered && registered->initialValue())
-            property = registered->initialValue();
-        else
-            return resolveVariableFallback(registeredProperties, range, result, style);
-    }
-    ASSERT(property);
-    result.appendVector(property->tokens(registeredProperties, style));
-    
-    return true;
-}
-
-RefPtr<CSSVariableData> CSSVariableData::resolveVariableReferences(const CSSRegisteredCustomPropertySet& registeredProperties, const RenderStyle& style) const
-{
-    Vector<CSSParserToken> resolvedTokens;
-    CSSParserTokenRange range = m_tokens;
-    if (!resolveTokenRange(registeredProperties, range, resolvedTokens, style))
-        return nullptr;
-    return CSSVariableData::createResolved(resolvedTokens, *this);
-}
-    
-bool CSSVariableData::resolveTokenRange(const CSSRegisteredCustomPropertySet& registeredProperties, CSSParserTokenRange range, Vector<CSSParserToken>& result, const RenderStyle& style) const
-{
-    bool success = true;
-    while (!range.atEnd()) {
-        if (range.peek().functionId() == CSSValueVar || range.peek().functionId() == CSSValueEnv)
-            success &= resolveVariableReference(registeredProperties, range.consumeBlock(), result, style);
-        else
-            result.append(range.consume());
-    }
-    return success;
-}
-
 } // namespace WebCore
index 008a6fa..396e069 100644 (file)
@@ -43,14 +43,9 @@ class CSSVariableData : public RefCounted<CSSVariableData> {
     WTF_MAKE_NONCOPYABLE(CSSVariableData);
     WTF_MAKE_FAST_ALLOCATED;
 public:
-    static Ref<CSSVariableData> create(const CSSParserTokenRange& range, bool needsVariableResolution = true)
+    static Ref<CSSVariableData> create(const CSSParserTokenRange& range)
     {
-        return adoptRef(*new CSSVariableData(range, needsVariableResolution));
-    }
-
-    static Ref<CSSVariableData> createResolved(const Vector<CSSParserToken>& resolvedTokens, const CSSVariableData& unresolvedData)
-    {
-        return adoptRef(*new CSSVariableData(resolvedTokens, unresolvedData.m_backingString));
+        return adoptRef(*new CSSVariableData(range));
     }
 
     CSSParserTokenRange tokenRange() { return m_tokens; }
@@ -59,36 +54,12 @@ public:
 
     bool operator==(const CSSVariableData& other) const;
 
-    bool needsVariableResolution() const { return m_needsVariableResolution; }
-
-    bool checkVariablesForCycles(const AtomicString& name, const RenderStyle&, HashSet<AtomicString>& seenProperties, HashSet<AtomicString>& invalidProperties) const;
-
-    RefPtr<CSSVariableData> resolveVariableReferences(const CSSRegisteredCustomPropertySet&, const RenderStyle&) const;
-    bool resolveTokenRange(const CSSRegisteredCustomPropertySet&, CSSParserTokenRange, Vector<CSSParserToken>&, const RenderStyle&) const;
-
 private:
-    CSSVariableData(const CSSParserTokenRange&, bool needsVariableResolution);
-
-    // We can safely copy the tokens (which have raw pointers to substrings) because
-    // StylePropertySets contain references to CSSCustomPropertyValues, which
-    // point to the unresolved CSSVariableData values that own the backing strings
-    // this will potentially reference.
-    CSSVariableData(const Vector<CSSParserToken>& resolvedTokens, String backingString)
-        : m_backingString(backingString)
-        , m_tokens(resolvedTokens)
-        , m_needsVariableResolution(false)
-    { }
-
-    void consumeAndUpdateTokens(const CSSParserTokenRange&);
+    CSSVariableData(const CSSParserTokenRange&);
     template<typename CharacterType> void updateTokens(const CSSParserTokenRange&);
-    
-    bool checkVariablesForCyclesWithRange(CSSParserTokenRange, const RenderStyle&, HashSet<AtomicString>& seenProperties, HashSet<AtomicString>& invalidProperties) const;
-    bool resolveVariableReference(const CSSRegisteredCustomPropertySet&, CSSParserTokenRange, Vector<CSSParserToken>&, const RenderStyle&) const;
-    bool resolveVariableFallback(const CSSRegisteredCustomPropertySet&, CSSParserTokenRange, Vector<CSSParserToken>&, const RenderStyle&) const;
 
     String m_backingString;
     Vector<CSSParserToken> m_tokens;
-    const bool m_needsVariableResolution;
 
     // FIXME-NEWPARSER: We want to cache StyleProperties once we support @apply.
 };
index 38957e4..77a523e 100644 (file)
@@ -30,6 +30,9 @@
 #include "config.h"
 #include "CSSVariableReferenceValue.h"
 
+#include "RenderStyle.h"
+#include "StyleResolver.h"
+
 namespace WebCore {
 
 String CSSVariableReferenceValue::customCSSText() const
@@ -40,11 +43,72 @@ String CSSVariableReferenceValue::customCSSText() const
     }
     return m_stringValue;
 }
-    
-bool CSSVariableReferenceValue::checkVariablesForCycles(const AtomicString& name, const RenderStyle& style, HashSet<AtomicString>& seenProperties, HashSet<AtomicString>& invalidProperties) const
+
+static bool resolveTokenRange(CSSParserTokenRange, Vector<CSSParserToken>&, ApplyCascadedPropertyState&);
+
+static bool resolveVariableFallback(CSSParserTokenRange range, Vector<CSSParserToken>& result, ApplyCascadedPropertyState& state)
+{
+    if (range.atEnd())
+        return false;
+    ASSERT(range.peek().type() == CommaToken);
+    range.consume();
+    return resolveTokenRange(range, result, state);
+}
+
+static bool resolveVariableReference(CSSParserTokenRange range, Vector<CSSParserToken>& result, ApplyCascadedPropertyState& state)
 {
-    ASSERT(m_data);
-    return m_data->checkVariablesForCycles(name, style, seenProperties, invalidProperties);
+    auto& registeredProperties = state.styleResolver->document().getCSSRegisteredCustomPropertySet();
+    auto& style = *state.styleResolver->style();
+
+    range.consumeWhitespace();
+    ASSERT(range.peek().type() == IdentToken);
+    String variableName = range.consumeIncludingWhitespace().value().toString();
+    ASSERT(range.atEnd() || (range.peek().type() == CommaToken));
+
+    // Apply this variable first, in case it is still unresolved
+    state.styleResolver->applyCascadedCustomProperty(variableName, state);
+
+    // Apply fallback to detect cycles
+    Vector<CSSParserToken> fallbackResult;
+    resolveVariableFallback(CSSParserTokenRange(range), fallbackResult, state);
+
+    auto* property = style.getCustomProperty(variableName);
+
+    if (!property || property->isUnset() || property->isInvalid()) {
+        auto* registered = registeredProperties.get(variableName);
+        if (registered && registered->initialValue())
+            property = registered->initialValue();
+        else
+            return resolveVariableFallback(range, result, state);
+    }
+
+    ASSERT(property->isResolved());
+    result.appendVector(property->tokens());
+
+    return true;
+}
+
+static bool resolveTokenRange(CSSParserTokenRange range, Vector<CSSParserToken>& result, ApplyCascadedPropertyState& state)
+{
+    bool success = true;
+    while (!range.atEnd()) {
+        if (range.peek().functionId() == CSSValueVar || range.peek().functionId() == CSSValueEnv)
+            success &= resolveVariableReference(range.consumeBlock(), result, state);
+        else
+            result.append(range.consume());
+    }
+    return success;
+}
+
+RefPtr<CSSVariableData> CSSVariableReferenceValue::resolveVariableReferences(ApplyCascadedPropertyState& state) const
+{
+    Vector<CSSParserToken> resolvedTokens;
+    CSSParserTokenRange range = m_data->tokenRange();
+
+    if (!resolveTokenRange(range, resolvedTokens, state))
+        return nullptr;
+
+    return CSSVariableData::create(resolvedTokens);
 }
 
 } // namespace WebCore
index 228964e..d70e327 100644 (file)
 
 #pragma once
 
+#include "CSSParserToken.h"
+#include "CSSParserTokenRange.h"
 #include "CSSValue.h"
 #include "CSSVariableData.h"
+#include <wtf/HashSet.h>
 #include <wtf/RefPtr.h>
+#include <wtf/text/WTFString.h>
 
 namespace WebCore {
 
+struct ApplyCascadedPropertyState;
+class CSSParserTokenRange;
+
 class CSSVariableReferenceValue : public CSSValue {
 public:
-    static Ref<CSSVariableReferenceValue> create(Ref<CSSVariableData>&& data)
-    {
-        return adoptRef(*new CSSVariableReferenceValue(WTFMove(data)));
-    }
-
-    CSSVariableData* variableDataValue() const
+    static Ref<CSSVariableReferenceValue> create(const CSSParserTokenRange& range)
     {
-        return m_data.get();
+        return adoptRef(*new CSSVariableReferenceValue(CSSVariableData::create(range)));
     }
 
-    bool equals(const CSSVariableReferenceValue& other) const { return m_data == other.m_data; }
+    bool equals(const CSSVariableReferenceValue& other) const { return m_data.get() == other.m_data.get(); }
     String customCSSText() const;
 
-    bool checkVariablesForCycles(const AtomicString& name, const RenderStyle&, HashSet<AtomicString>& seenProperties, HashSet<AtomicString>& invalidProperties) const;
+    RefPtr<CSSVariableData> resolveVariableReferences(ApplyCascadedPropertyState&) const;
 
 private:
     CSSVariableReferenceValue(Ref<CSSVariableData>&& data)
@@ -59,7 +61,7 @@ private:
     {
     }
 
-    RefPtr<CSSVariableData> m_data;
+    Ref<CSSVariableData> m_data;
     mutable String m_stringValue;
     mutable bool m_serialized { false };
 };
index 37318da..b8ef6a9 100644 (file)
 
 #include "CSSCustomPropertyValue.h"
 #include "CSSPropertyNames.h"
+#include "CSSPropertyParser.h"
 #include "CSSRegisteredCustomProperty.h"
 #include "CSSTokenizer.h"
 #include "DOMCSSNamespace.h"
 #include "Document.h"
 #include "StyleBuilderConverter.h"
-#include "StyleResolver.h"
 #include <wtf/text/WTFString.h>
 
 namespace WebCore {
@@ -42,28 +42,33 @@ ExceptionOr<void> DOMCSSRegisterCustomProperty::registerProperty(Document& docum
 {
     RefPtr<CSSCustomPropertyValue> initialValue;
     if (!descriptor.initialValue.isEmpty()) {
-        initialValue = CSSCustomPropertyValue::createWithVariableData(descriptor.name, CSSVariableData::create(CSSParserTokenRange(Vector<CSSParserToken>()), false));
-        CSSParser parser(document);
+        CSSTokenizer tokenizer(descriptor.initialValue);
         StyleResolver styleResolver(document);
 
-        auto primitiveVal = parser.parseSingleValue(CSSPropertyCustom, descriptor.initialValue);
-        if (!primitiveVal || !primitiveVal->isPrimitiveValue())
+        // We need to initialize this so that we can successfully parse computationally dependent values (like em units).
+        // We don't actually need the values to be accurate, since they will be rejected later anyway
+        styleResolver.state().setStyle(styleResolver.defaultStyleForElement());
+        styleResolver.updateFont();
+
+        initialValue = CSSPropertyParser::parseTypedCustomPropertyValue(descriptor.name, descriptor.syntax, tokenizer.tokenRange(), styleResolver, strictCSSParserContext());
+
+        if (!initialValue || !initialValue->isResolved())
             return Exception { SyntaxError, "The given initial value does not parse for the given syntax." };
 
         HashSet<CSSPropertyID> dependencies;
-        primitiveVal->collectDirectComputationalDependencies(dependencies);
-        primitiveVal->collectDirectRootComputationalDependencies(dependencies);
+        initialValue->collectDirectComputationalDependencies(dependencies);
+        initialValue->collectDirectRootComputationalDependencies(dependencies);
 
         if (!dependencies.isEmpty())
             return Exception { SyntaxError, "The given initial value must be computationally independent." };
-
-        initialValue->setResolvedTypedValue(StyleBuilderConverter::convertLength(styleResolver, *primitiveVal));
     }
 
-    CSSRegisteredCustomProperty property { descriptor.name, descriptor.inherits, WTFMove(initialValue) };
+    CSSRegisteredCustomProperty property { descriptor.name, descriptor.syntax, descriptor.inherits, WTFMove(initialValue) };
     if (!document.registerCSSProperty(WTFMove(property)))
         return Exception { InvalidModificationError, "This property has already been registered." };
 
+    document.styleScope().didChangeStyleSheetEnvironment();
+
     return { };
 }
 
index e28b34c..383ac8e 100644 (file)
@@ -226,6 +226,16 @@ bool PropertySetCSSStyleDeclaration::isPropertyImplicit(const String& propertyNa
 ExceptionOr<void> PropertySetCSSStyleDeclaration::setProperty(const String& propertyName, const String& value, const String& priority)
 {
     StyleAttributeMutationScope mutationScope(this);
+
+    if (!willMutate())
+        return { };
+
+    Document* document = nullptr;
+
+    if (parentElement())
+        document = &parentElement()->document();
+    else
+        document = parentStyleSheet()->ownerDocument();
     
     CSSPropertyID propertyID = cssPropertyID(propertyName);
     if (isCustomPropertyName(propertyName))
@@ -233,16 +243,13 @@ ExceptionOr<void> PropertySetCSSStyleDeclaration::setProperty(const String& prop
     if (!propertyID)
         return { };
 
-    if (!willMutate())
-        return { };
-
     bool important = equalIgnoringASCIICase(priority, "important");
     if (!important && !priority.isEmpty())
         return { };
 
     bool changed;
     if (propertyID == CSSPropertyCustom)
-        changed = m_propertySet->setCustomProperty(propertyName, value, important, cssParserContext());
+        changed = m_propertySet->setCustomProperty(document, propertyName, value, important, cssParserContext());
     else
         changed = m_propertySet->setProperty(propertyID, value, important, cssParserContext());
 
index 0c47da2..2000fdc 100644 (file)
@@ -1879,13 +1879,13 @@ inline void StyleBuilderCustom::applyValueStrokeColor(StyleResolver& styleResolv
 
 inline void StyleBuilderCustom::applyInitialCustomProperty(StyleResolver& styleResolver, const CSSRegisteredCustomProperty* registered, const AtomicString& name)
 {
-    if (registered) {
+    if (registered && registered->initialValue()) {
         auto initialValue = registered->initialValueCopy();
         applyValueCustomProperty(styleResolver, registered, *initialValue);
         return;
     }
 
-    auto invalid = CSSCustomPropertyValue::createWithID(name, CSSValueInvalid);
+    auto invalid = CSSCustomPropertyValue::createUnresolved(name, CSSValueInvalid);
     applyValueCustomProperty(styleResolver, registered, invalid.get());
 }
 
@@ -1900,12 +1900,13 @@ inline void StyleBuilderCustom::applyInheritCustomProperty(StyleResolver& styleR
 
 inline void StyleBuilderCustom::applyValueCustomProperty(StyleResolver& styleResolver, const CSSRegisteredCustomProperty* registered, CSSCustomPropertyValue& value)
 {
+    ASSERT(value.isResolved());
     const auto& name = value.name();
 
     if (!registered || registered->inherits)
-        styleResolver.style()->setInheritedCustomPropertyValue(name, makeRef(value), registered);
+        styleResolver.style()->setInheritedCustomPropertyValue(name, makeRef(value));
     else
-        styleResolver.style()->setNonInheritedCustomPropertyValue(name, makeRef(value), registered);
+        styleResolver.style()->setNonInheritedCustomPropertyValue(name, makeRef(value));
 }
 
 } // namespace WebCore
index 5d78f8c..5acb48f 100644 (file)
@@ -28,6 +28,8 @@
 #include "CSSDeferredParser.h"
 #include "CSSParser.h"
 #include "CSSPendingSubstitutionValue.h"
+#include "CSSPropertyParser.h"
+#include "CSSTokenizer.h"
 #include "CSSValueKeywords.h"
 #include "CSSValueList.h"
 #include "CSSValuePool.h"
@@ -779,7 +781,7 @@ bool MutableStyleProperties::setProperty(CSSPropertyID propertyID, const String&
     return setProperty(propertyID, value, important, parserContext);
 }
 
-bool MutableStyleProperties::setCustomProperty(const String& propertyName, const String& value, bool important, CSSParserContext parserContext)
+bool MutableStyleProperties::setCustomProperty(const Document* document, const String& propertyName, const String& value, bool important, CSSParserContext parserContext)
 {
     // Setting the value to an empty string just removes the property in both IE and Gecko.
     // Setting it to null seems to produce less consistent results, but we treat it just the same.
@@ -787,6 +789,17 @@ bool MutableStyleProperties::setCustomProperty(const String& propertyName, const
         return removeCustomProperty(propertyName);
 
     parserContext.mode = cssParserMode();
+
+    String syntax = "*";
+    auto* registered = document ? document->getCSSRegisteredCustomPropertySet().get(propertyName) : nullptr;
+
+    if (registered)
+        syntax = registered->syntax;
+
+    CSSTokenizer tokenizer(value);
+    if (!CSSPropertyParser::canParseTypedCustomPropertyValue(syntax, tokenizer.tokenRange(), parserContext))
+        return false;
+
     // When replacing an existing property value, this moves the property to the end of the list.
     // Firefox preserves the position, and MSIE moves the property to the beginning.
     return CSSParser::parseCustomPropertyValue(*this, propertyName, value, important, parserContext) == CSSParser::ParseResult::Changed;
index 3f4b65a..839a544 100644 (file)
@@ -249,7 +249,7 @@ public:
     Vector<CSSProperty, 4> m_propertyVector;
 
     // Methods for querying and altering CSS custom properties.
-    bool setCustomProperty(const String& propertyName, const String& value, bool important, CSSParserContext);
+    bool setCustomProperty(const Document*, const String& propertyName, const String& value, bool important, CSSParserContext);
     bool removeCustomProperty(const String& propertyName, String* returnText = nullptr);
 
 private:
index d4e06bc..20f5b2e 100644 (file)
@@ -321,7 +321,7 @@ inline void StyleResolver::State::updateConversionData()
     m_cssToLengthConversionData = CSSToLengthConversionData(m_style.get(), m_rootElementStyle, m_element ? m_element->document().renderView() : nullptr);
 }
 
-inline void StyleResolver::State::setStyle(std::unique_ptr<RenderStyle> style)
+void StyleResolver::State::setStyle(std::unique_ptr<RenderStyle> style)
 {
     m_style = WTFMove(style);
     updateConversionData();
@@ -429,21 +429,21 @@ std::unique_ptr<RenderStyle> StyleResolver::styleForKeyframe(const RenderStyle*
     CascadedProperties cascade(direction, writingMode);
     cascade.addNormalMatches(result, 0, result.matchedProperties().size() - 1);
 
-    // Resolve custom properties first.
-    applyCascadedProperties(cascade, CSSPropertyCustom, CSSPropertyCustom, &result);
-
-    applyCascadedProperties(cascade, firstCSSProperty, lastHighPriorityProperty, &result);
+    ApplyCascadedPropertyState applyState { this, &cascade, &result };
+    applyCascadedProperties(firstCSSProperty, lastHighPriorityProperty, applyState);
 
     // If our font got dirtied, update it now.
     updateFont();
 
-    // Now do rest of the properties.
-    applyCascadedProperties(cascade, firstLowPriorityProperty, lastCSSProperty, &result);
+    // Now resolve remaining custom properties and the rest, in any order
+    for (auto it = cascade.customProperties().begin(); it != cascade.customProperties().end(); ++it)
+        applyCascadedCustomProperty(it->key, applyState);
+    applyCascadedProperties(firstLowPriorityProperty, lastCSSProperty, applyState);
 
     // If our font got dirtied by one of the non-essential font props, update it a second time.
     updateFont();
 
-    cascade.applyDeferredProperties(*this, &result);
+    cascade.applyDeferredProperties(*this, applyState);
 
     adjustRenderStyle(*state.style(), *state.parentStyle(), nullptr, nullptr);
 
@@ -635,17 +635,18 @@ std::unique_ptr<RenderStyle> StyleResolver::styleForPage(int pageIndex)
     CascadedProperties cascade(direction, writingMode);
     cascade.addNormalMatches(result, 0, result.matchedProperties().size() - 1);
 
-    // Resolve custom properties first.
-    applyCascadedProperties(cascade, CSSPropertyCustom, CSSPropertyCustom, &result);
-
-    applyCascadedProperties(cascade, firstCSSProperty, lastHighPriorityProperty, &result);
+    ApplyCascadedPropertyState applyState { this, &cascade, &result };
+    applyCascadedProperties(firstCSSProperty, lastHighPriorityProperty, applyState);
 
     // If our font got dirtied, update it now.
     updateFont();
 
-    applyCascadedProperties(cascade, firstLowPriorityProperty, lastCSSProperty, &result);
+    // Now resolve remaining custom properties and the rest, in any order
+    for (auto it = cascade.customProperties().begin(); it != cascade.customProperties().end(); ++it)
+        applyCascadedCustomProperty(it->key, applyState);
+    applyCascadedProperties(firstLowPriorityProperty, lastCSSProperty, applyState);
 
-    cascade.applyDeferredProperties(*this, &result);
+    cascade.applyDeferredProperties(*this, applyState);
 
     // Now return the style.
     return m_state.takeStyle();
@@ -1371,17 +1372,20 @@ void StyleResolver::applyMatchedProperties(const MatchResult& matchResult, const
         cascade.addNormalMatches(matchResult, matchResult.ranges.firstUARule, matchResult.ranges.lastUARule, applyInheritedOnly);
         cascade.addImportantMatches(matchResult, matchResult.ranges.firstUARule, matchResult.ranges.lastUARule, applyInheritedOnly);
 
-        applyCascadedProperties(cascade, CSSPropertyWebkitRubyPosition, CSSPropertyWebkitRubyPosition, &matchResult);
-        adjustStyleForInterCharacterRuby();
+        ApplyCascadedPropertyState applyState { this, &cascade, &matchResult };
 
-        // Resolve custom variables first.
-        applyCascadedProperties(cascade, CSSPropertyCustom, CSSPropertyCustom, &matchResult);
+        applyCascadedProperties(CSSPropertyWebkitRubyPosition, CSSPropertyWebkitRubyPosition, applyState);
+        adjustStyleForInterCharacterRuby();
 
-        // Start by applying properties that other properties may depend on.
-        applyCascadedProperties(cascade, firstCSSProperty, lastHighPriorityProperty, &matchResult);
+        applyCascadedProperties(firstCSSProperty, lastHighPriorityProperty, applyState);
 
+        // If our font got dirtied, update it now.
         updateFont();
-        applyCascadedProperties(cascade, firstLowPriorityProperty, lastCSSProperty, &matchResult);
+
+        // Now resolve remaining custom properties and the rest, in any order
+        for (auto it = cascade.customProperties().begin(); it != cascade.customProperties().end(); ++it)
+            applyCascadedCustomProperty(it->key, applyState);
+        applyCascadedProperties(firstLowPriorityProperty, lastCSSProperty, applyState);
 
         state.cacheBorderAndBackground();
     }
@@ -1392,16 +1396,13 @@ void StyleResolver::applyMatchedProperties(const MatchResult& matchResult, const
     cascade.addImportantMatches(matchResult, matchResult.ranges.firstUserRule, matchResult.ranges.lastUserRule, applyInheritedOnly);
     cascade.addImportantMatches(matchResult, matchResult.ranges.firstUARule, matchResult.ranges.lastUARule, applyInheritedOnly);
 
-    // Resolve custom properties first.
-    applyCascadedProperties(cascade, CSSPropertyCustom, CSSPropertyCustom, &matchResult);
 
-    applyCascadedProperties(cascade, CSSPropertyWebkitRubyPosition, CSSPropertyWebkitRubyPosition, &matchResult);
+    ApplyCascadedPropertyState applyState { this, &cascade, &matchResult };
 
-    // Adjust the font size to be smaller if ruby-position is inter-character.
+    applyCascadedProperties(CSSPropertyWebkitRubyPosition, CSSPropertyWebkitRubyPosition, applyState);
     adjustStyleForInterCharacterRuby();
 
-    // Start by applying properties that other properties may depend on.
-    applyCascadedProperties(cascade, firstCSSProperty, lastHighPriorityProperty, &matchResult);
+    applyCascadedProperties(firstCSSProperty, lastHighPriorityProperty, applyState);
 
     // If the effective zoom value changes, we can't use the matched properties cache. Start over.
     if (cacheItem && cacheItem->renderStyle->effectiveZoom() != state.style()->effectiveZoom())
@@ -1414,13 +1415,15 @@ void StyleResolver::applyMatchedProperties(const MatchResult& matchResult, const
     if (cacheItem && cacheItem->renderStyle->fontDescription() != state.style()->fontDescription())
         return applyMatchedProperties(matchResult, element, DoNotUseMatchedPropertiesCache);
 
-    // Apply properties that no other properties depend on.
-    applyCascadedProperties(cascade, firstLowPriorityProperty, lastCSSProperty, &matchResult);
+    // Now resolve remaining custom properties and the rest, in any order
+    for (auto it = cascade.customProperties().begin(); it != cascade.customProperties().end(); ++it)
+        applyCascadedCustomProperty(it->key, applyState);
+    applyCascadedProperties(firstLowPriorityProperty, lastCSSProperty, applyState);
 
     // Finally, some properties must be applied in the order they were parsed.
     // There are some CSS properties that affect the same RenderStyle values,
     // so to preserve behavior, we queue them up during cascade and flush here.
-    cascade.applyDeferredProperties(*this, &matchResult);
+    cascade.applyDeferredProperties(*this, applyState);
 
     ASSERT(!state.fontDirty());
 
@@ -1441,8 +1444,9 @@ void StyleResolver::applyPropertyToStyle(CSSPropertyID id, CSSValue* value, std:
 
 void StyleResolver::applyPropertyToCurrentStyle(CSSPropertyID id, CSSValue* value)
 {
+    ApplyCascadedPropertyState applyState { this, nullptr, nullptr };
     if (value)
-        applyProperty(id, value);
+        applyProperty(id, value, applyState);
 }
 
 inline bool isValidVisitedLinkProperty(CSSPropertyID id)
@@ -1609,16 +1613,18 @@ StyleResolver::CascadedProperties* StyleResolver::cascadedPropertiesForRollback(
     return nullptr;
 }
 
-void StyleResolver::applyProperty(CSSPropertyID id, CSSValue* value, SelectorChecker::LinkMatchMask linkMatchMask, const MatchResult* matchResult)
+void StyleResolver::applyProperty(CSSPropertyID id, CSSValue* value, ApplyCascadedPropertyState& applyState, SelectorChecker::LinkMatchMask linkMatchMask)
 {
+    auto* matchResult = applyState.matchResult;
     ASSERT_WITH_MESSAGE(!isShorthandCSSProperty(id), "Shorthand property id = %d wasn't expanded at parsing time", id);
 
     State& state = m_state;
 
     RefPtr<CSSValue> valueToApply = value;
     if (value->hasVariableReferences()) {
-        valueToApply = resolvedVariableValue(id, *value);
-        if (!valueToApply) {
+        valueToApply = resolvedVariableValue(id, *value, applyState);
+        // If appliedProperties already has this id, then we detected a cycle, and this value should be unset.
+        if (!valueToApply || applyState.appliedProperties.contains(id)) {
             if (CSSProperty::isInheritedProperty(id))
                 valueToApply = CSSValuePool::singleton().createInheritedValue();
             else
@@ -1629,7 +1635,7 @@ void StyleResolver::applyProperty(CSSPropertyID id, CSSValue* value, SelectorChe
     if (CSSProperty::isDirectionAwareProperty(id)) {
         CSSPropertyID newId = CSSProperty::resolveDirectionAwareProperty(id, state.style()->direction(), state.style()->writingMode());
         ASSERT(newId != id);
-        return applyProperty(newId, valueToApply.get(), linkMatchMask, matchResult);
+        return applyProperty(newId, valueToApply.get(), applyState, linkMatchMask);
     }
 
     CSSValue* valueToCheckForInheritInitial = valueToApply.get();
@@ -1640,7 +1646,9 @@ void StyleResolver::applyProperty(CSSPropertyID id, CSSValue* value, SelectorChe
 
     if (id == CSSPropertyCustom) {
         customPropertyValue = &downcast<CSSCustomPropertyValue>(*valueToApply);
-        customPropertyValueID = customPropertyValue->valueID();
+        ASSERT(customPropertyValue->isResolved());
+        if (WTF::holds_alternative<CSSValueID>(customPropertyValue->value()))
+            customPropertyValueID = WTF::get<CSSValueID>(customPropertyValue->value());
         auto& name = customPropertyValue->name();
         customPropertyRegistered = document().getCSSRegisteredCustomPropertySet().get(name);
     }
@@ -1667,13 +1675,13 @@ void StyleResolver::applyProperty(CSSPropertyID id, CSSValue* value, SelectorChe
                 if (customPropertyRegistered && customPropertyRegistered->inherits && rollback->hasCustomProperty(customPropertyValue->name())) {
                     auto property = rollback->customProperty(customPropertyValue->name());
                     if (property.cssValue[linkMatchMask])
-                        applyProperty(property.id, property.cssValue[linkMatchMask], linkMatchMask, matchResult);
+                        applyProperty(property.id, property.cssValue[linkMatchMask], applyState, linkMatchMask);
                     return;
                 }
             } else if (rollback->hasProperty(id)) {
                 auto& property = rollback->property(id);
                 if (property.cssValue[linkMatchMask])
-                    applyProperty(property.id, property.cssValue[linkMatchMask], linkMatchMask, matchResult);
+                    applyProperty(property.id, property.cssValue[linkMatchMask], applyState, linkMatchMask);
                 return;
             }
 
@@ -1702,10 +1710,10 @@ void StyleResolver::applyProperty(CSSPropertyID id, CSSValue* value, SelectorChe
     StyleBuilder::applyProperty(id, *this, *valueToApply, isInitial, isInherit, customPropertyRegistered);
 }
 
-RefPtr<CSSValue> StyleResolver::resolvedVariableValue(CSSPropertyID propID, const CSSValue& value) const
+RefPtr<CSSValue> StyleResolver::resolvedVariableValue(CSSPropertyID propID, const CSSValue& value, ApplyCascadedPropertyState& state) const
 {
     CSSParser parser(document());
-    return parser.parseValueWithVariableReferences(propID, value, document().getCSSRegisteredCustomPropertySet(), *m_state.style());
+    return parser.parseValueWithVariableReferences(propID, value, state);
 }
 
 RefPtr<StyleImage> StyleResolver::styleImage(CSSValue& value)
@@ -2227,13 +2235,13 @@ void StyleResolver::CascadedProperties::addImportantMatches(const MatchResult& m
         addMatch(matchResult, match.index, true, inheritedOnly);
 }
 
-void StyleResolver::CascadedProperties::applyDeferredProperties(StyleResolver& resolver, const MatchResult* matchResult)
+void StyleResolver::CascadedProperties::applyDeferredProperties(StyleResolver& resolver, ApplyCascadedPropertyState& applyState)
 {
     for (auto& property : m_deferredProperties)
-        property.apply(resolver, matchResult);
+        property.apply(resolver, applyState);
 }
 
-void StyleResolver::CascadedProperties::Property::apply(StyleResolver& resolver, const MatchResult* matchResult)
+void StyleResolver::CascadedProperties::Property::apply(StyleResolver& resolver, ApplyCascadedPropertyState& applyState)
 {
     State& state = resolver.state();
     state.setCascadeLevel(level);
@@ -2242,7 +2250,7 @@ void StyleResolver::CascadedProperties::Property::apply(StyleResolver& resolver,
     if (cssValue[SelectorChecker::MatchDefault]) {
         state.setApplyPropertyToRegularStyle(true);
         state.setApplyPropertyToVisitedLinkStyle(false);
-        resolver.applyProperty(id, cssValue[SelectorChecker::MatchDefault], SelectorChecker::MatchDefault, matchResult);
+        resolver.applyProperty(id, cssValue[SelectorChecker::MatchDefault], applyState, SelectorChecker::MatchDefault);
     }
 
     if (state.style()->insideLink() == InsideLink::NotInside)
@@ -2251,43 +2259,99 @@ void StyleResolver::CascadedProperties::Property::apply(StyleResolver& resolver,
     if (cssValue[SelectorChecker::MatchLink]) {
         state.setApplyPropertyToRegularStyle(true);
         state.setApplyPropertyToVisitedLinkStyle(false);
-        resolver.applyProperty(id, cssValue[SelectorChecker::MatchLink], SelectorChecker::MatchLink, matchResult);
+        resolver.applyProperty(id, cssValue[SelectorChecker::MatchLink], applyState, SelectorChecker::MatchLink);
     }
 
     if (cssValue[SelectorChecker::MatchVisited]) {
         state.setApplyPropertyToRegularStyle(false);
         state.setApplyPropertyToVisitedLinkStyle(true);
-        resolver.applyProperty(id, cssValue[SelectorChecker::MatchVisited], SelectorChecker::MatchVisited, matchResult);
+        resolver.applyProperty(id, cssValue[SelectorChecker::MatchVisited], applyState, SelectorChecker::MatchVisited);
     }
 
     state.setApplyPropertyToRegularStyle(true);
     state.setApplyPropertyToVisitedLinkStyle(false);
 }
 
-void StyleResolver::applyCascadedProperties(CascadedProperties& cascade, int firstProperty, int lastProperty, const MatchResult* matchResult)
+void StyleResolver::applyCascadedCustomProperty(const String& name, ApplyCascadedPropertyState& state)
+{
+    if (state.appliedCustomProperties.contains(name) || !state.cascade->customProperties().contains(name))
+        return;
+
+    auto property = state.cascade->customProperties().get(name);
+
+    for (auto index : { SelectorChecker::MatchDefault, SelectorChecker::MatchLink, SelectorChecker::MatchVisited }) {
+        if (!property.cssValue[index])
+            continue;
+        if (index != SelectorChecker::MatchDefault && this->state().style()->insideLink() == InsideLink::NotInside)
+            continue;
+
+        Ref<CSSCustomPropertyValue> valueToApply = CSSCustomPropertyValue::create(downcast<CSSCustomPropertyValue>(*property.cssValue[index]));
+
+        if (state.inProgressPropertiesCustom.contains(name)) {
+            // We are in a cycle, so reset the value.
+            state.appliedCustomProperties.add(name);
+            // Resolve this value so that we reset its dependencies
+            if (WTF::holds_alternative<Ref<CSSVariableReferenceValue>>(valueToApply->value()))
+                resolvedVariableValue(CSSPropertyCustom, valueToApply.get(), state);
+            valueToApply = CSSCustomPropertyValue::createWithID(name, CSSValueUnset);
+        }
+
+        state.inProgressPropertiesCustom.add(name);
+
+        if (WTF::holds_alternative<Ref<CSSVariableReferenceValue>>(valueToApply->value())) {
+            RefPtr<CSSValue> parsedValue = resolvedVariableValue(CSSPropertyCustom, valueToApply.get(), state);
+
+            if (!parsedValue)
+                parsedValue = CSSCustomPropertyValue::createWithID(name, CSSValueUnset);
+
+            valueToApply = downcast<CSSCustomPropertyValue>(*parsedValue);
+        }
+
+        if (state.inProgressPropertiesCustom.contains(name)) {
+            if (index == SelectorChecker::MatchDefault) {
+                this->state().setApplyPropertyToRegularStyle(true);
+                this->state().setApplyPropertyToVisitedLinkStyle(false);
+            }
+
+            if (index == SelectorChecker::MatchLink) {
+                this->state().setApplyPropertyToRegularStyle(true);
+                this->state().setApplyPropertyToVisitedLinkStyle(false);
+            }
+
+            if (index == SelectorChecker::MatchVisited) {
+                this->state().setApplyPropertyToRegularStyle(false);
+                this->state().setApplyPropertyToVisitedLinkStyle(true);
+            }
+            applyProperty(CSSPropertyCustom, valueToApply.ptr(), state, index);
+        }
+        state.inProgressPropertiesCustom.remove(name);
+        state.appliedCustomProperties.add(name);
+    }
+}
+
+void StyleResolver::applyCascadedProperties(int firstProperty, int lastProperty, ApplyCascadedPropertyState& state)
 {
     for (int id = firstProperty; id <= lastProperty; ++id) {
         CSSPropertyID propertyID = static_cast<CSSPropertyID>(id);
-        if (!cascade.hasProperty(propertyID))
+        if (!state.cascade->hasProperty(propertyID))
             continue;
-        if (propertyID == CSSPropertyCustom) {
-            // Apply font size first, since custom properties may rely on it for relative unit calculations
-            // Once it is applied and the custom property values are set, it will be applied again, which should
-            // handle any font-size properties that rely on registered custom properties.
-            // FIXME cycles should be detected.
-            if (cascade.hasProperty(CSSPropertyFontSize))
-                cascade.property(CSSPropertyFontSize).apply(*this, matchResult);
-            updateFont();
-
-            HashMap<AtomicString, CascadedProperties::Property>::iterator end = cascade.customProperties().end();
-            for (HashMap<AtomicString, CascadedProperties::Property>::iterator it = cascade.customProperties().begin(); it != end; ++it)
-                it->value.apply(*this, matchResult);
-            m_state.style()->checkVariablesInCustomProperties(document().getCSSRegisteredCustomPropertySet(), m_state.parentStyle(), *this);
+        ASSERT(propertyID != CSSPropertyCustom);
+        auto& property = state.cascade->property(propertyID);
+        ASSERT(!shouldApplyPropertyInParseOrder(propertyID));
+
+        if (state.inProgressProperties.contains(propertyID)) {
+            // We are in a cycle (eg. setting font size using registered custom property value containing em)
+            // So this value should be unset
+            state.appliedProperties.add(propertyID);
+            // This property is in a cycle, and only the root of the call stack will have firstProperty != lastProperty.
+            ASSERT(firstProperty == lastProperty);
             continue;
         }
-        auto& property = cascade.property(propertyID);
-        ASSERT(!shouldApplyPropertyInParseOrder(propertyID));
-        property.apply(*this, matchResult);
+
+        state.inProgressProperties.add(propertyID);
+        property.apply(*this, state);
+        state.appliedProperties.add(propertyID);
+        state.inProgressProperties.remove(propertyID);
     }
 }
 
index 4703cef..6d88f60 100644 (file)
@@ -267,7 +267,7 @@ public:
         CascadedProperties(TextDirection, WritingMode);
 
         struct Property {
-            void apply(StyleResolver&, const MatchResult*);
+            void apply(StyleResolver&, ApplyCascadedPropertyState&);
 
             CSSPropertyID id;
             CascadeLevel level;
@@ -281,7 +281,7 @@ public:
         void addNormalMatches(const MatchResult&, int startIndex, int endIndex, bool inheritedOnly = false);
         void addImportantMatches(const MatchResult&, int startIndex, int endIndex, bool inheritedOnly = false);
 
-        void applyDeferredProperties(StyleResolver&, const MatchResult*);
+        void applyDeferredProperties(StyleResolver&, ApplyCascadedPropertyState&);
 
         HashMap<AtomicString, Property>& customProperties() { return m_customProperties; }
         bool hasCustomProperty(const String&) const;
@@ -302,7 +302,10 @@ public:
         TextDirection m_direction;
         WritingMode m_writingMode;
     };
-    
+
+    void applyCascadedProperties(int firstProperty, int lastProperty, ApplyCascadedPropertyState&);
+    void applyCascadedCustomProperty(const String& name, ApplyCascadedPropertyState&);
+
 private:
     // This function fixes up the default font size if it detects that the current generic font family has changed. -dwh
     void checkForGenericFamilyChange(RenderStyle*, const RenderStyle* parentStyle);
@@ -321,7 +324,6 @@ private:
     enum ShouldUseMatchedPropertiesCache { DoNotUseMatchedPropertiesCache = 0, UseMatchedPropertiesCache };
     void applyMatchedProperties(const MatchResult&, const Element&, ShouldUseMatchedPropertiesCache = UseMatchedPropertiesCache);
 
-    void applyCascadedProperties(CascadedProperties&, int firstProperty, int lastProperty, const MatchResult*);
     void cascadeMatches(CascadedProperties&, const MatchResult&, bool important, int startIndex, int endIndex, bool inheritedOnly);
 
     void matchPageRules(MatchResult&, RuleSet*, bool isLeftPage, bool isFirstPage, const String& pageName);
@@ -455,12 +457,12 @@ public:
     void setWritingMode(WritingMode writingMode) { m_state.setWritingMode(writingMode); }
     void setTextOrientation(TextOrientation textOrientation) { m_state.setTextOrientation(textOrientation); }
 
-    RefPtr<CSSValue> resolvedVariableValue(CSSPropertyID, const CSSValue&) const;
+    RefPtr<CSSValue> resolvedVariableValue(CSSPropertyID, const CSSValue&, ApplyCascadedPropertyState&) const;
 
 private:
     void cacheBorderAndBackground();
 
-    void applyProperty(CSSPropertyID, CSSValue*, SelectorChecker::LinkMatchMask = SelectorChecker::MatchDefault, const MatchResult* = nullptr);
+    void applyProperty(CSSPropertyID, CSSValue*, ApplyCascadedPropertyState&, SelectorChecker::LinkMatchMask = SelectorChecker::MatchDefault);
 
     void applySVGProperty(CSSPropertyID, CSSValue*);
 
@@ -524,6 +526,19 @@ private:
     friend bool operator!=(const MatchRanges&, const MatchRanges&);
 };
 
+// State to use when applying properties, to keep track of which custom and high-priority
+// properties have been resolved.
+struct ApplyCascadedPropertyState {
+    StyleResolver* styleResolver;
+    StyleResolver::CascadedProperties* cascade;
+    const StyleResolver::MatchResult* matchResult;
+    HashSet<CSSPropertyID> appliedProperties = { };
+    HashSet<String> appliedCustomProperties = { };
+    HashSet<CSSPropertyID> inProgressProperties = { };
+    HashSet<String> inProgressPropertiesCustom = { };
+};
+
+
 inline bool StyleResolver::hasSelectorForAttribute(const Element& element, const AtomicString &attributeName) const
 {
     ASSERT(!attributeName.isEmpty());
index 9df0fd4..0d6aef0 100644 (file)
@@ -47,6 +47,7 @@
 #include "RuntimeEnabledFeatures.h"
 #include "Settings.h"
 #include "StyleColor.h"
+#include "StyleResolver.h"
 #include "StyleRule.h"
 #include "StyleSheetContents.h"
 #include <wtf/NeverDestroyed.h>
@@ -177,8 +178,10 @@ void CSSParser::parseDeclarationForInspector(const CSSParserContext& context, co
     CSSParserImpl::parseDeclarationListForInspector(string, context, observer);
 }
 
-RefPtr<CSSValue> CSSParser::parseValueWithVariableReferences(CSSPropertyID propID, const CSSValue& value, const CSSRegisteredCustomPropertySet& registeredProperties, const RenderStyle& style)
+RefPtr<CSSValue> CSSParser::parseValueWithVariableReferences(CSSPropertyID propID, const CSSValue& value, ApplyCascadedPropertyState& state)
 {
+    ASSERT((propID == CSSPropertyCustom && value.isCustomPropertyValue()) || (propID != CSSPropertyCustom && !value.isCustomPropertyValue()));
+    auto& style = *state.styleResolver->style();
     auto direction = style.direction();
     auto writingMode = style.writingMode();
 
@@ -190,12 +193,11 @@ RefPtr<CSSValue> CSSParser::parseValueWithVariableReferences(CSSPropertyID propI
         if (CSSProperty::isDirectionAwareProperty(shorthandID))
             shorthandID = CSSProperty::resolveDirectionAwareProperty(shorthandID, direction, writingMode);
         CSSVariableReferenceValue* shorthandValue = pendingSubstitution.shorthandValue();
-        const CSSVariableData* variableData = shorthandValue->variableDataValue();
-        ASSERT(variableData);
-        
-        Vector<CSSParserToken> resolvedTokens;
-        if (!variableData->resolveTokenRange(registeredProperties, variableData->tokens(), resolvedTokens, style))
+
+        auto resolvedData = shorthandValue->resolveVariableReferences(state);
+        if (!resolvedData)
             return nullptr;
+        Vector<CSSParserToken> resolvedTokens = resolvedData->tokens();
         
         ParsedPropertyVector parsedProperties;
         if (!CSSPropertyParser::parseValue(shorthandID, false, resolvedTokens, m_context, parsedProperties, StyleRule::Style))
@@ -209,31 +211,33 @@ RefPtr<CSSValue> CSSParser::parseValueWithVariableReferences(CSSPropertyID propI
         return nullptr;
     }
 
-    const CSSVariableData* variableData = nullptr;
-
     if (value.isVariableReferenceValue()) {
         const CSSVariableReferenceValue& valueWithReferences = downcast<CSSVariableReferenceValue>(value);
-        variableData = valueWithReferences.variableDataValue();
-        ASSERT(variableData);
+        auto resolvedData = valueWithReferences.resolveVariableReferences(state);
+        if (!resolvedData)
+            return nullptr;
+        return CSSPropertyParser::parseSingleValue(propID, resolvedData->tokens(), m_context);
     }
 
-    if (value.isCustomPropertyValue()) {
-        const CSSCustomPropertyValue& customPropValue = downcast<CSSCustomPropertyValue>(value);
+    const auto& customPropValue = downcast<CSSCustomPropertyValue>(value);
+    const auto& valueWithReferences = WTF::get<Ref<CSSVariableReferenceValue>>(customPropValue.value()).get();
 
-        if (customPropValue.resolvedTypedValue())
-            return CSSPrimitiveValue::create(*customPropValue.resolvedTypedValue(), style);
+    auto& name = downcast<CSSCustomPropertyValue>(value).name();
+    auto* registered = state.styleResolver->document().getCSSRegisteredCustomPropertySet().get(name);
+    auto& syntax = registered ? registered->syntax : "*";
+    auto resolvedData = valueWithReferences.resolveVariableReferences(state);
 
-        variableData = customPropValue.value();
-    }
-
-    if (!variableData)
+    if (!resolvedData)
         return nullptr;
 
-    Vector<CSSParserToken> resolvedTokens;
-    if (!variableData->resolveTokenRange(registeredProperties, variableData->tokens(), resolvedTokens, style))
-        return nullptr;
+    // FIXME handle REM cycles.
+    HashSet<CSSPropertyID> dependencies;
+    CSSPropertyParser::collectParsedCustomPropertyValueDependencies(syntax, false, dependencies, resolvedData->tokens(), m_context);
+
+    for (auto id : dependencies)
+        state.styleResolver->applyCascadedProperties(id, id, state);
 
-    return CSSPropertyParser::parseSingleValue(propID, resolvedTokens, m_context);
+    return CSSPropertyParser::parseTypedCustomPropertyValue(name, syntax, resolvedData->tokens(), *state.styleResolver, m_context);
 }
 
 std::unique_ptr<Vector<double>> CSSParser::parseKeyframeKeyList(const String& selector)
index 09ba4b0..47063cd 100644 (file)
@@ -30,6 +30,7 @@
 
 namespace WebCore {
 
+struct ApplyCascadedPropertyState;
 class CSSParserObserver;
 class CSSSelectorList;
 class Color;
@@ -77,7 +78,7 @@ public:
 
     void parseSelector(const String&, CSSSelectorList&);
 
-    RefPtr<CSSValue> parseValueWithVariableReferences(CSSPropertyID, const CSSValue&, const CSSRegisteredCustomPropertySet&, const RenderStyle&);
+    RefPtr<CSSValue> parseValueWithVariableReferences(CSSPropertyID, const CSSValue&, ApplyCascadedPropertyState&);
 
     static Color parseColor(const String&, bool strict = false);
     static Color parseSystemColor(const String&, const CSSParserContext*);
index 21febac..c3a23b5 100644 (file)
@@ -37,6 +37,7 @@
 #include "CSSContentDistributionValue.h"
 #include "CSSCursorImageValue.h"
 #include "CSSCustomIdentValue.h"
+#include "CSSCustomPropertyValue.h"
 #include "CSSFontFaceSrcValue.h"
 #include "CSSFontFeatureValue.h"
 #if ENABLE(VARIATION_FONTS)
 #include "RuntimeEnabledFeatures.h"
 #include "SVGPathByteStream.h"
 #include "SVGPathUtilities.h"
+#include "StyleBuilderConverter.h"
 #include "StylePropertyShorthand.h"
 #include "StylePropertyShorthandFunctions.h"
+#include "StyleResolver.h"
 #include <bitset>
 #include <memory>
 #include <wtf/text/StringBuilder.h>
@@ -213,12 +216,13 @@ CSSPropertyID cssPropertyID(StringView string)
     
 using namespace CSSPropertyParserHelpers;
 
-CSSPropertyParser::CSSPropertyParser(const CSSParserTokenRange& range, const CSSParserContext& context, Vector<CSSProperty, 256>* parsedProperties)
+CSSPropertyParser::CSSPropertyParser(const CSSParserTokenRange& range, const CSSParserContext& context, Vector<CSSProperty, 256>* parsedProperties, bool consumeWhitespace)
     : m_range(range)
     , m_context(context)
     , m_parsedProperties(parsedProperties)
 {
-    m_range.consumeWhitespace();
+    if (consumeWhitespace)
+        m_range.consumeWhitespace();
 }
 
 void CSSPropertyParser::addProperty(CSSPropertyID property, CSSPropertyID currentShorthand, Ref<CSSValue>&& value, bool important, bool implicit)
@@ -278,6 +282,27 @@ RefPtr<CSSValue> CSSPropertyParser::parseSingleValue(CSSPropertyID property, con
     return value;
 }
 
+bool CSSPropertyParser::canParseTypedCustomPropertyValue(const String& syntax, const CSSParserTokenRange& tokens, const CSSParserContext& context)
+{
+    CSSPropertyParser parser(tokens, context, nullptr);
+    return parser.canParseTypedCustomPropertyValue(syntax);
+}
+
+RefPtr<CSSCustomPropertyValue> CSSPropertyParser::parseTypedCustomPropertyValue(const String& name, const String& syntax, const CSSParserTokenRange& tokens, const StyleResolver& styleResolver, const CSSParserContext& context)
+{
+    CSSPropertyParser parser(tokens, context, nullptr, false);
+    RefPtr<CSSCustomPropertyValue> value = parser.parseTypedCustomPropertyValue(name, syntax, styleResolver);
+    if (!value || !parser.m_range.atEnd())
+        return nullptr;
+    return value;
+}
+
+void CSSPropertyParser::collectParsedCustomPropertyValueDependencies(const String& syntax, bool isRoot, HashSet<CSSPropertyID>& dependencies, const CSSParserTokenRange& tokens, const CSSParserContext& context)
+{
+    CSSPropertyParser parser(tokens, context, nullptr);
+    parser.collectParsedCustomPropertyValueDependencies(syntax, isRoot, dependencies);
+}
+
 static bool isLegacyBreakProperty(CSSPropertyID propertyID)
 {
     switch (propertyID) {
@@ -320,7 +345,7 @@ bool CSSPropertyParser::parseValueStart(CSSPropertyID propertyID, bool important
     }
 
     if (CSSVariableParser::containsValidVariableReferences(originalRange, m_context)) {
-        RefPtr<CSSVariableReferenceValue> variable = CSSVariableReferenceValue::create(CSSVariableData::create(originalRange));
+        RefPtr<CSSVariableReferenceValue> variable = CSSVariableReferenceValue::create(originalRange);
 
         if (isShorthand) {
             RefPtr<CSSPendingSubstitutionValue> pendingValue = CSSPendingSubstitutionValue::create(propertyID, variable.releaseNonNull());
@@ -3886,7 +3911,6 @@ RefPtr<CSSValue> CSSPropertyParser::parseSingleValue(CSSPropertyID property, CSS
     case CSSPropertyMinHeight:
     case CSSPropertyWidth:
     case CSSPropertyHeight:
-    case CSSPropertyCustom: // FIXME: parsing a registered custom property should be based on its syntax.
         return consumeWidthOrHeight(m_range, m_context, UnitlessQuirk::Allow);
     case CSSPropertyMinInlineSize:
     case CSSPropertyMinBlockSize:
@@ -4256,6 +4280,64 @@ RefPtr<CSSValue> CSSPropertyParser::parseSingleValue(CSSPropertyID property, CSS
     }
 }
 
+bool CSSPropertyParser::canParseTypedCustomPropertyValue(const String& syntax)
+{
+    if (syntax != "*") {
+        m_range.consumeWhitespace();
+
+        // First check for keywords
+        CSSValueID id = m_range.peek().id();
+        if (id == CSSValueInherit || id == CSSValueInitial || id == CSSValueRevert)
+            return true;
+
+        auto localRange = m_range;
+        while (!localRange.atEnd()) {
+            auto id = localRange.consume().functionId();
+            if (id == CSSValueVar || id == CSSValueEnv)
+                return true; // For variables, we just permit everything
+        }
+
+        auto primitiveVal = consumeWidthOrHeight(m_range, m_context);
+        if (primitiveVal && primitiveVal->isPrimitiveValue() && m_range.atEnd())
+            return true;
+        return false;
+    }
+
+    return true;
+}
+
+void CSSPropertyParser::collectParsedCustomPropertyValueDependencies(const String& syntax, bool isRoot, HashSet<CSSPropertyID>& dependencies)
+{
+    if (syntax != "*") {
+        m_range.consumeWhitespace();
+        auto primitiveVal = consumeWidthOrHeight(m_range, m_context);
+        if (!m_range.atEnd())
+            return;
+        if (primitiveVal && primitiveVal->isPrimitiveValue()) {
+            primitiveVal->collectDirectComputationalDependencies(dependencies);
+            if (isRoot)
+                primitiveVal->collectDirectRootComputationalDependencies(dependencies);
+        }
+    }
+}
+
+RefPtr<CSSCustomPropertyValue> CSSPropertyParser::parseTypedCustomPropertyValue(const String& name, const String& syntax, const StyleResolver& styleResolver)
+{
+    if (syntax != "*") {
+        m_range.consumeWhitespace();
+        auto primitiveVal = consumeWidthOrHeight(m_range, m_context);
+        if (primitiveVal && primitiveVal->isPrimitiveValue())
+            return CSSCustomPropertyValue::createSyntaxLength(name, StyleBuilderConverter::convertLength(styleResolver, *primitiveVal));
+    } else {
+        auto propertyValue = CSSCustomPropertyValue::createSyntaxAll(name, CSSVariableData::create(m_range));
+        while (!m_range.atEnd())
+            m_range.consume();
+        return { WTFMove(propertyValue) };
+    }
+
+    return nullptr;
+}
+
 static RefPtr<CSSValueList> consumeFontFaceUnicodeRange(CSSParserTokenRange& range)
 {
     RefPtr<CSSValueList> values = CSSValueList::createCommaSeparated();
index 7db7453..1dc05ef 100644 (file)
@@ -32,6 +32,7 @@ class CSSProperty;
 class CSSValue;
 class StylePropertyShorthand;
 class StyleSheetContents;
+class StyleResolver;
     
 // Inputs: PropertyID, isImportant bool, CSSParserTokenRange.
 // Outputs: Vector of CSSProperties
@@ -45,14 +46,20 @@ public:
 
     // Parses a non-shorthand CSS property
     static RefPtr<CSSValue> parseSingleValue(CSSPropertyID, const CSSParserTokenRange&, const CSSParserContext&);
+    static bool canParseTypedCustomPropertyValue(const String& syntax, const CSSParserTokenRange&, const CSSParserContext&);
+    static RefPtr<CSSCustomPropertyValue> parseTypedCustomPropertyValue(const String& name, const String& syntax, const CSSParserTokenRange&, const StyleResolver&, const CSSParserContext&);
+    static void collectParsedCustomPropertyValueDependencies(const String& syntax, bool isRoot, HashSet<CSSPropertyID>& dependencies, const CSSParserTokenRange&, const CSSParserContext&);
 
 private:
-    CSSPropertyParser(const CSSParserTokenRange&, const CSSParserContext&, Vector<CSSProperty, 256>*);
+    CSSPropertyParser(const CSSParserTokenRange&, const CSSParserContext&, Vector<CSSProperty, 256>*, bool consumeWhitespace = true);
 
     // FIXME: Rename once the CSSParserValue-based parseValue is removed
     bool parseValueStart(CSSPropertyID, bool important);
     bool consumeCSSWideKeyword(CSSPropertyID, bool important);
     RefPtr<CSSValue> parseSingleValue(CSSPropertyID, CSSPropertyID = CSSPropertyInvalid);
+    bool canParseTypedCustomPropertyValue(const String& syntax);
+    RefPtr<CSSCustomPropertyValue> parseTypedCustomPropertyValue(const String& name, const String& syntax, const StyleResolver&);
+    void collectParsedCustomPropertyValueDependencies(const String& syntax, bool isRoot, HashSet<CSSPropertyID>& dependencies);
 
     bool inQuirksMode() const { return m_context.mode == HTMLQuirksMode; }
 
index 87086e3..1be23b9 100644 (file)
@@ -189,8 +189,8 @@ RefPtr<CSSCustomPropertyValue> CSSVariableParser::parseDeclarationValue(const At
     if (type == CSSValueInvalid)
         return nullptr;
     if (type == CSSValueInternalVariableValue)
-        return CSSCustomPropertyValue::createWithVariableData(variableName, CSSVariableData::create(range, hasReferences || hasAtApplyRule));
-    return CSSCustomPropertyValue::createWithID(variableName, type);
+        return CSSCustomPropertyValue::createUnresolved(variableName, CSSVariableReferenceValue::create(range));
+    return CSSCustomPropertyValue::createUnresolved(variableName, type);
 }
 
 } // namespace WebCore
index 90fd139..6c108c8 100644 (file)
@@ -90,7 +90,7 @@ void ConstantPropertyMap::setValueForProperty(ConstantProperty property, Ref<CSS
         buildValues();
 
     auto& name = nameForProperty(property);
-    m_values->set(name, CSSCustomPropertyValue::createWithVariableData(name, WTFMove(data)));
+    m_values->set(name, CSSCustomPropertyValue::createSyntaxAll(name, WTFMove(data)));
 }
 
 void ConstantPropertyMap::buildValues()
@@ -110,7 +110,7 @@ static Ref<CSSVariableData> variableDataForPositivePixelLength(float lengthInPx)
 
     Vector<CSSParserToken> tokens { token };
     CSSParserTokenRange tokenRange(tokens);
-    return CSSVariableData::create(tokenRange, false);
+    return CSSVariableData::create(tokenRange);
 }
 
 static Ref<CSSVariableData> variableDataForPositiveDuration(Seconds durationInSeconds)
@@ -122,7 +122,7 @@ static Ref<CSSVariableData> variableDataForPositiveDuration(Seconds durationInSe
 
     Vector<CSSParserToken> tokens { token };
     CSSParserTokenRange tokenRange(tokens);
-    return CSSVariableData::create(tokenRange, false);
+    return CSSVariableData::create(tokenRange);
 }
 
 void ConstantPropertyMap::updateConstantsForSafeAreaInsets()
index db85d7d..c852588 100644 (file)
@@ -2299,89 +2299,6 @@ bool RenderStyle::hasReferenceFilterOnly() const
     return filterOperations.size() == 1 && filterOperations.at(0)->type() == FilterOperation::REFERENCE;
 }
 
-void RenderStyle::checkVariablesInCustomProperties(const CSSRegisteredCustomPropertySet& registeredProperties, const RenderStyle* parentStyle, const StyleResolver& styleResolver)
-{
-    bool shouldUpdateInherited = m_rareInheritedData->customProperties->containsVariables || m_rareInheritedData->customProperties->containsUnresolvedRegisteredProperties;
-    bool shouldUpdateNonInherited = m_rareNonInheritedData->customProperties->containsVariables || m_rareNonInheritedData->customProperties->containsUnresolvedRegisteredProperties;
-    auto* inheritedPropertyData = shouldUpdateInherited ? &m_rareInheritedData.access().customProperties.access() : nullptr;
-    auto* nonInheritedPropertyData = shouldUpdateNonInherited ? &m_rareNonInheritedData.access().customProperties.access() : nullptr;
-
-    HashSet<AtomicString> invalidProperties;
-
-    for (auto* customPropertyData : { inheritedPropertyData, nonInheritedPropertyData }) {
-        if (!customPropertyData)
-            continue;
-
-        // Our first pass checks the variables for validity and replaces any properties that became
-        // invalid with empty values.
-        auto& customProperties = customPropertyData->values;
-        for (auto& entry : customProperties) {
-            if (!entry.value->containsVariables())
-                continue;
-            HashSet<AtomicString> seenProperties;
-            entry.value->checkVariablesForCycles(entry.key, *this, seenProperties, invalidProperties);
-        }
-
-        auto invalidValue = CSSCustomPropertyValue::createInvalid();
-
-        // Now insert invalid values, or defaults if the property is registered.
-        if (!invalidProperties.isEmpty()) {
-            for (auto& property : invalidProperties) {
-                if (!customProperties.contains(property))
-                    continue;
-                
-                const RefPtr<CSSCustomPropertyValue>* parentProperty = nullptr;
-                if (parentStyle) {
-                    auto iterator = parentStyle->inheritedCustomProperties().find(property);
-                    if (iterator != parentStyle->inheritedCustomProperties().end() && iterator.get() && iterator.get()->value)
-                        parentProperty = &iterator.get()->value;
-                }
-
-                auto* registered = registeredProperties.get(property);
-
-                if (registered && registered->inherits && parentProperty)
-                    customProperties.set(property, parentProperty->copyRef());
-                else if (registered && registered->initialValue())
-                    customProperties.set(property, registered->initialValueCopy());
-                else
-                    customProperties.set(property, invalidValue.copyRef());
-            }
-        }
-
-        // Now that all of the properties have been tested for validity and replaced with
-        // invalid values if they failed, we can perform variable substitution on the valid values.
-        Vector<Ref<CSSCustomPropertyValue>> resolvedValues;
-        for (auto entry : customProperties) {
-            if (!entry.value->containsVariables())
-                continue;
-            entry.value->resolveVariableReferences(registeredProperties, resolvedValues, *this);
-        }
-
-        // With all results computed, we can now mutate our table to eliminate the variables and
-        // hold the final values. This way when we inherit, we don't end up resubstituting variables, etc.
-        for (auto& resolvedValue : resolvedValues)
-            customProperties.set(resolvedValue->name(), resolvedValue.copyRef());
-
-        // Finally we can resolve registered custom properties to their typed values.
-        for (auto& entry : customProperties) {
-            auto& name = entry.value->name();
-            auto* registered = registeredProperties.get(name);
-
-            if (!registered)
-                continue;
-
-            auto primitiveVal = styleResolver.resolvedVariableValue(CSSPropertyCustom, *entry.value);
-            if (!primitiveVal || !primitiveVal->isPrimitiveValue())
-                entry.value = invalidValue.copyRef();
-            else
-                entry.value->setResolvedTypedValue(StyleBuilderConverter::convertLength(styleResolver, *primitiveVal));
-        }
-
-        customPropertyData->containsVariables = false;
-        customPropertyData->containsUnresolvedRegisteredProperties = false;
-    }
-}
-
 float RenderStyle::outlineWidth() const
 {
     if (m_backgroundData->outline.style() == BorderStyle::None)
index dd242e7..708e6bf 100644 (file)
@@ -186,8 +186,8 @@ public:
     const CustomPropertyValueMap& inheritedCustomProperties() const { return m_rareInheritedData->customProperties->values; }
     const CustomPropertyValueMap& nonInheritedCustomProperties() const { return m_rareNonInheritedData->customProperties->values; }
     const CSSCustomPropertyValue* getCustomProperty(const AtomicString&) const;
-    void setInheritedCustomPropertyValue(const AtomicString& name, Ref<CSSCustomPropertyValue>&& value, bool isRegistered = false) { return m_rareInheritedData.access().customProperties.access().setCustomPropertyValue(name, WTFMove(value), isRegistered); }
-    void setNonInheritedCustomPropertyValue(const AtomicString& name, Ref<CSSCustomPropertyValue>&& value, bool isRegistered = false) { return m_rareNonInheritedData.access().customProperties.access().setCustomPropertyValue(name, WTFMove(value), isRegistered); }
+    void setInheritedCustomPropertyValue(const AtomicString& name, Ref<CSSCustomPropertyValue>&& value) { return m_rareInheritedData.access().customProperties.access().setCustomPropertyValue(name, WTFMove(value)); }
+    void setNonInheritedCustomPropertyValue(const AtomicString& name, Ref<CSSCustomPropertyValue>&& value) { return m_rareNonInheritedData.access().customProperties.access().setCustomPropertyValue(name, WTFMove(value)); }
 
     void setHasViewportUnits(bool v = true) { m_nonInheritedFlags.hasViewportUnits = v; }
     bool hasViewportUnits() const { return m_nonInheritedFlags.hasViewportUnits; }
@@ -795,8 +795,6 @@ public:
     ApplePayButtonType applePayButtonType() const { return static_cast<ApplePayButtonType>(m_rareNonInheritedData->applePayButtonType); }
 #endif
 
-    void checkVariablesInCustomProperties(const CSSRegisteredCustomPropertySet&, const RenderStyle* parentStyle, const StyleResolver&);
-
 // attribute setter methods
 
     void setDisplay(DisplayType v) { m_nonInheritedFlags.effectiveDisplay = static_cast<unsigned>(v); }
index 1bcdb22..132e6f5 100644 (file)
@@ -22,6 +22,7 @@
 #pragma once
 
 #include "CSSCustomPropertyValue.h"
+#include "CSSVariableReferenceValue.h"
 #include <wtf/Forward.h>
 #include <wtf/HashMap.h>
 #include <wtf/RefCounted.h>
@@ -37,9 +38,6 @@ public:
     
     bool operator==(const StyleCustomPropertyData& other) const
     {
-        if (containsVariables != other.containsVariables)
-            return false;
-
         if (values.size() != other.values.size())
             return false;
 
@@ -54,26 +52,18 @@ public:
 
     bool operator!=(const StyleCustomPropertyData& other) const { return !(*this == other); }
     
-    void setCustomPropertyValue(const AtomicString& name, Ref<CSSCustomPropertyValue>&& value, bool isRegistered)
+    void setCustomPropertyValue(const AtomicString& name, Ref<CSSCustomPropertyValue>&& value)
     {
-        if (value->containsVariables())
-            containsVariables = true;
-        if (isRegistered)
-            containsUnresolvedRegisteredProperties = true;
         values.set(name, WTFMove(value));
     }
 
     CustomPropertyValueMap values;
-    bool containsVariables { false };
-    bool containsUnresolvedRegisteredProperties { false };
 
 private:
     StyleCustomPropertyData() = default;
     StyleCustomPropertyData(const StyleCustomPropertyData& other)
         : RefCounted<StyleCustomPropertyData>()
         , values(other.values)
-        , containsVariables(other.containsVariables)
-        , containsUnresolvedRegisteredProperties(other.containsUnresolvedRegisteredProperties)
     { }
 };