Skip to content
Permalink
Browse files
[@Property] Support list values
https://bugs.webkit.org/show_bug.cgi?id=249216
<rdar://problem/103300866>

Reviewed by Chris Dumez.

Consume values with <type>+ and <type># syntax definitions.

* 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-property-computation-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/css/css-properties-values-api/registered-property-initial-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/css/css-properties-values-api/typedom-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/css/css-properties-values-api/var-reference-registered-properties-expected.txt:
* Source/WebCore/css/CSSCustomPropertyValue.cpp:
(WebCore::CSSCustomPropertyValue::equals const):
(WebCore::CSSCustomPropertyValue::customCSSText const):
* Source/WebCore/css/CSSCustomPropertyValue.h:

Add list value support.
Handle ident values with SyntaxValue too.
Use URL type for URLs.

* Source/WebCore/css/CSSValue.cpp:
(WebCore::CSSValue::collectDirectComputationalDependencies const):
(WebCore::CSSValue::collectDirectRootComputationalDependencies const):

Collect from lists too.

* Source/WebCore/css/DOMCSSRegisterCustomProperty.cpp:
(WebCore::DOMCSSRegisterCustomProperty::registerProperty):

Test for null initial value string instead of empty (empty string needs to be considered as an initial value).
Remove unnecessary collectDirect*ComputationalDependencies calls.

* Source/WebCore/css/parser/CSSPropertyParser.cpp:
(WebCore::CSSPropertyParser::consumeCustomPropertyValueWithSyntax):

Consume lists.

(WebCore::CSSPropertyParser::collectParsedCustomPropertyValueDependencies):
(WebCore::CSSPropertyParser::parseTypedCustomPropertyValue):

Resolve syntax values in lists.

Canonical link: https://commits.webkit.org/257844@main
  • Loading branch information
anttijk committed Dec 14, 2022
1 parent a8076cd commit f266283a31249f27645c470313b8ee346c4f0daa
Show file tree
Hide file tree
Showing 11 changed files with 170 additions and 123 deletions.
@@ -5,8 +5,8 @@ PASS syntax:'<length>', initialValue:'2px' is valid
PASS syntax:' <number>', initialValue:'5' is valid
PASS syntax:'<percentage> ', initialValue:'10%' is valid
PASS syntax:'<color>+', initialValue:'red' is valid
FAIL syntax:' <length>+ | <percentage>', initialValue:'2px 8px' is valid The given initial value does not parse for the given syntax.
FAIL syntax:' <length>+ | <color>#', initialValue:'red, blue' is valid The given initial value does not parse for the given syntax.
PASS syntax:' <length>+ | <percentage>', initialValue:'2px 8px' is valid
PASS syntax:' <length>+ | <color>#', initialValue:'red, blue' is valid
PASS syntax:'<length>|<percentage>|<length-percentage>', initialValue:'2px' is valid
PASS syntax:'<color> | <image> | <url> | <integer> | <angle>', initialValue:'red' is valid
PASS syntax:'<time> | <resolution> | <transform-list> | <custom-ident>', initialValue:'red' is valid
@@ -23,14 +23,14 @@ PASS syntax:'<length>', initialValue:' calc(-2px)' is valid
PASS syntax:'<length>', initialValue:'calc(2px*4 + 10px)' is valid
PASS syntax:'<length>', initialValue:'7.1e-4cm' is valid
PASS syntax:'<length>', initialValue:'calc(7in - 12px)' is valid
FAIL syntax:'<length>+', initialValue:'2px 7px calc(8px)' is valid The given initial value does not parse for the given syntax.
FAIL syntax:'<length>#', initialValue:'2px, 7px, calc(8px)' is valid The given initial value does not parse for the given syntax.
PASS syntax:'<length>+', initialValue:'2px 7px calc(8px)' is valid
PASS syntax:'<length>#', initialValue:'2px, 7px, calc(8px)' is valid
PASS syntax:'<percentage>', initialValue:'-9.3e3%' is valid
PASS syntax:'<length-percentage>', initialValue:'-54%' is valid
PASS syntax:'<length-percentage>', initialValue:'0' is valid
PASS syntax:'<length-percentage>', initialValue:'calc(-11px + 10.4%)' is valid
PASS syntax:'<length>', initialValue:'10vmin' is valid
FAIL syntax:'<percentage> | <length>+', initialValue:'calc(100vh - 10px) 30px' is valid The given initial value does not parse for the given syntax.
PASS syntax:'<percentage> | <length>+', initialValue:'calc(100vh - 10px) 30px' is valid
PASS syntax:'<number>', initialValue:'-109' is valid
PASS syntax:'<number>', initialValue:'2.3e4' is valid
PASS syntax:'<integer>', initialValue:'-109' is valid
@@ -65,7 +65,7 @@ PASS syntax:'banana', initialValue:'banan\61' is valid
PASS syntax:'banan\61', initialValue:'banana' is valid
PASS syntax:'<custom-ident>', initialValue:'banan\61' is valid
PASS syntax:'big | bigger | BIGGER', initialValue:'bigger' is valid
FAIL syntax:'foo+|bar', initialValue:'foo foo foo' is valid The given initial value does not parse for the given syntax.
PASS syntax:'foo+|bar', initialValue:'foo foo foo' is valid
PASS syntax:'banana ', initialValue:'banana' is valid
PASS syntax:'
banana\r
@@ -157,8 +157,8 @@ 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
PASS syntax:'<length>+', initialValue:'10px calc(20px + 4rem)' is invalid
FAIL syntax:'<length>+', initialValue:'' is invalid assert_throws_dom: function "() => CSS.registerProperty({name: name, syntax: syntax, initialValue: initialValue, inherits: false})" did not throw
FAIL syntax:'<length>#', initialValue:'' is invalid assert_throws_dom: function "() => CSS.registerProperty({name: name, syntax: syntax, initialValue: initialValue, inherits: false})" did not throw
PASS syntax:'<length>+', initialValue:'' is invalid
PASS syntax:'<length>#', initialValue:'' is invalid
PASS syntax:'<length>', initialValue:'10px;' is invalid
PASS syntax:'<length-percentage>', initialValue:'calc(2px + 10% + 7ex)' is invalid
PASS syntax:'<percentage>', initialValue:'0' is invalid
@@ -17,16 +17,16 @@ PASS <length> values are computed correctly [10lh]
PASS <length-percentage> values are computed correctly [17em]
PASS <length-percentage> values are computed correctly [18%]
PASS <length-percentage> values are computed correctly [calc(19em - 2%)]
FAIL <length># values are computed correctly [10px, 3em] assert_equals: expected "10px, 30px" but got "0px"
FAIL <length># values are computed correctly [4em ,9px] assert_equals: expected "40px, 9px" but got "0px"
PASS <length># values are computed correctly [10px, 3em]
PASS <length># values are computed correctly [4em ,9px]
PASS <length># values are computed correctly [8em]
FAIL <length-percentage># values are computed correctly [3% , 10vmax , 22px] assert_equals: expected "3%, 80px, 22px" but got "0px"
FAIL <length-percentage># values are computed correctly [calc(50% + 1em), 4px] assert_equals: expected "calc(50% + 10px), 4px" but got "0px"
PASS <length-percentage># values are computed correctly [3% , 10vmax , 22px]
PASS <length-percentage># values are computed correctly [calc(50% + 1em), 4px]
PASS <length-percentage># values are computed correctly [calc(13% + 37px)]
FAIL <length>+ values are computed correctly [10px 3em] assert_equals: expected "10px 30px" but got "0px"
FAIL <length>+ values are computed correctly [4em 9px] assert_equals: expected "40px 9px" but got "0px"
FAIL <length-percentage>+ values are computed correctly [3% 10vmax 22px] assert_equals: expected "3% 80px 22px" but got "0px"
FAIL <length-percentage>+ values are computed correctly [calc(50% + 1em) 4px] assert_equals: expected "calc(50% + 10px) 4px" but got "0px"
PASS <length>+ values are computed correctly [10px 3em]
PASS <length>+ values are computed correctly [4em 9px]
PASS <length-percentage>+ values are computed correctly [3% 10vmax 22px]
PASS <length-percentage>+ values are computed correctly [calc(50% + 1em) 4px]
FAIL <transform-function> values are computed correctly [translateX(2px)] The given initial value does not parse for the given syntax.
FAIL <transform-function> values are computed correctly [translateX(10em)] The given initial value does not parse for the given syntax.
FAIL <transform-function> values are computed correctly [translateX(calc(11em + 10%))] The given initial value does not parse for the given syntax.
@@ -36,7 +36,7 @@ PASS <integer> values are computed correctly [calc(15 + 15)]
PASS <integer> values are computed correctly [calc(2.4)]
PASS <integer> values are computed correctly [calc(2.6)]
PASS <integer> values are computed correctly [calc(2.6 + 3.1)]
FAIL <integer>+ values are computed correctly [15 calc(2.4) calc(2.6)] assert_equals: expected "15 2 3" but got "0"
PASS <integer>+ values are computed correctly [15 calc(2.4) calc(2.6)]
PASS <color> values are computed correctly [#ff0000]
PASS <color> values are computed correctly [#000f00]
PASS <color> values are computed correctly [#00000a]
@@ -13,7 +13,7 @@ FAIL Initial value for <transform-function> correctly computed [rotate(42deg)] T
FAIL Initial value for <transform-list> correctly computed [scale(calc(2 + 2))] The given initial value does not parse for the given syntax.
FAIL Initial value for <transform-list> correctly computed [scale(calc(2 + 1)) translateX(calc(3px + 1px))] The given initial value does not parse for the given syntax.
PASS Initial value for <url> correctly computed [url(a)]
FAIL Initial value for <url>+ correctly computed [url(a) url(a)] The given initial value does not parse for the given syntax.
PASS Initial value for <url>+ correctly computed [url(a) url(a)]
PASS Initial inherited value can be substituted [purple, color]
PASS Initial non-inherited value can be substituted [pink, background-color]
PASS Initial non-inherited value can be substituted [ foo , --x]
@@ -17,8 +17,8 @@ FAIL Computed <url> is reified as CSSStyleValue assert_false: expected false got
FAIL Computed ident is reified as CSSKeywordValue assert_false: expected false got true
FAIL First computed value correctly reified in space-separated list assert_false: expected false got true
FAIL First computed value correctly reified in comma-separated list assert_false: expected false got true
FAIL All computed values correctly reified in space-separated list The given initial value does not parse for the given syntax.
FAIL All computed values correctly reified in comma-separated list The given initial value does not parse for the given syntax.
FAIL All computed values correctly reified in space-separated list assert_equals: expected 2 but got 1
FAIL All computed values correctly reified in comma-separated list assert_equals: expected 2 but got 1
PASS Specified * is reified as CSSUnparsedValue from get/getAll [attributeStyleMap]
PASS Specified * is reified as CSSUnparsedValue from get/getAll [styleMap]
PASS Specified foo is reified as CSSUnparsedValue from get/getAll [attributeStyleMap]
@@ -1,11 +1,11 @@

FAIL var() references work with registered properties assert_equals: expected " 20px" but got "20px"
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"
PASS References to registered var()-properties work in registered lists
PASS References to mixed registered and unregistered var()-properties work in registered lists
PASS Registered lists may be concatenated
PASS Font-relative units are absolutized when substituting
PASS Calc expressions are resolved when substituting
FAIL Lists with relative units are absolutized when substituting assert_equals: expected "110px, 120px" but got "0px"
PASS Lists with relative units are absolutized when substituting
FAIL Values are absolutized when substituting into properties with universal syntax assert_equals: expected " 100px" but got "100px"
PASS Valid fallback does not invalidate var()-reference [<length>, 10px]
PASS Valid fallback does not invalidate var()-reference [<length> | <color>, red]
@@ -58,6 +58,8 @@ bool CSSCustomPropertyValue::equals(const CSSCustomPropertyValue& other) const
return value.get() == std::get<Ref<CSSVariableData>>(other.m_value).get();
}, [&](const SyntaxValue& value) {
return value == std::get<SyntaxValue>(other.m_value);
}, [&](const SyntaxValueList& value) {
return value == std::get<SyntaxValueList>(other.m_value);
});
}

@@ -73,8 +75,10 @@ String CSSCustomPropertyValue::customCSSText() const
}, [&](const RefPtr<StyleImage>& value) {
// FIXME: This is not right for gradients that use `currentcolor`. There should be a way preserve it.
return value->computedStyleValue(RenderStyle::defaultStyle())->cssText();
}, [&](const URL& value) {
return serializeURL(value.string());
}, [&](const String& value) {
return serializeURL(value);
return value;
});
};

@@ -89,6 +93,15 @@ String CSSCustomPropertyValue::customCSSText() const
m_stringValue = value->tokenRange().serialize();
}, [&](const SyntaxValue& syntaxValue) {
m_stringValue = serializeSyntaxValue(syntaxValue);
}, [&](const SyntaxValueList& syntaxValueList) {
StringBuilder builder;
auto separator = separatorCSSText(syntaxValueList.separator);
for (auto& syntaxValue : syntaxValueList.values) {
if (!builder.isEmpty())
builder.append(separator);
builder.append(serializeSyntaxValue(syntaxValue));
}
m_stringValue = builder.toString();
});
}
return m_stringValue;
@@ -31,6 +31,7 @@
#include "Length.h"
#include "StyleColor.h"
#include "StyleImage.h"
#include <wtf/URL.h>

namespace WebCore {

@@ -42,15 +43,19 @@ class CSSCustomPropertyValue final : public CSSValue {
double value;
CSSUnitType unitType;

bool operator==(const NumericSyntaxValue& other) const
{
return value == other.value && unitType == other.unitType;
}
bool operator==(const NumericSyntaxValue& other) const { return value == other.value && unitType == other.unitType; }
};

using SyntaxValue = std::variant<Length, NumericSyntaxValue, StyleColor, RefPtr<StyleImage>, URL, String>;

struct SyntaxValueList {
Vector<SyntaxValue> values;
ValueSeparator separator;

bool operator==(const SyntaxValueList& other) const { return values == other.values && separator == other.separator; }
};
using SyntaxValue = std::variant<Length, NumericSyntaxValue, StyleColor, RefPtr<StyleImage>, String>;

using VariantValue = std::variant<std::monostate, Ref<CSSVariableReferenceValue>, CSSValueID, Ref<CSSVariableData>, SyntaxValue>;
using VariantValue = std::variant<std::monostate, Ref<CSSVariableReferenceValue>, CSSValueID, Ref<CSSVariableData>, SyntaxValue, SyntaxValueList>;

static Ref<CSSCustomPropertyValue> createEmpty(const AtomString& name);

@@ -71,30 +76,14 @@ class CSSCustomPropertyValue final : public CSSValue {
return adoptRef(*new CSSCustomPropertyValue(name, VariantValue { std::in_place_type<Ref<CSSVariableData>>, WTFMove(value) }));
}

static Ref<CSSCustomPropertyValue> createForLengthSyntax(const AtomString& name, Length value)
{
ASSERT(!value.isUndefined());
return adoptRef(*new CSSCustomPropertyValue(name, { SyntaxValue { WTFMove(value) } }));
}

static Ref<CSSCustomPropertyValue> createForNumericSyntax(const AtomString& name, double value, CSSUnitType unitType)
{
return adoptRef(*new CSSCustomPropertyValue(name, { SyntaxValue { NumericSyntaxValue { value, unitType } } }));
}

static Ref<CSSCustomPropertyValue> createForColorSyntax(const AtomString& name, StyleColor color)
{
return adoptRef(*new CSSCustomPropertyValue(name, { SyntaxValue { WTFMove(color) } }));
}

static Ref<CSSCustomPropertyValue> createForImageSyntax(const AtomString& name, RefPtr<StyleImage> image)
static Ref<CSSCustomPropertyValue> createForSyntaxValue(const AtomString& name, SyntaxValue&& syntaxValue)
{
return adoptRef(*new CSSCustomPropertyValue(name, { SyntaxValue { WTFMove(image) } }));
return adoptRef(*new CSSCustomPropertyValue(name, VariantValue { WTFMove(syntaxValue) }));
}

static Ref<CSSCustomPropertyValue> createForURLSyntax(const AtomString& name, String url)
static Ref<CSSCustomPropertyValue> createForSyntaxValueList(const AtomString& name, SyntaxValueList&& syntaxValueList)
{
return adoptRef(*new CSSCustomPropertyValue(name, { SyntaxValue { WTFMove(url) } }));
return adoptRef(*new CSSCustomPropertyValue(name, VariantValue { WTFMove(syntaxValueList) }));
}

static Ref<CSSCustomPropertyValue> create(const CSSCustomPropertyValue& other)
@@ -223,12 +223,24 @@ bool CSSValue::traverseSubresources(const Function<bool(const CachedResource&)>&

void CSSValue::collectDirectComputationalDependencies(HashSet<CSSPropertyID>& values) const
{
if (auto* asList = dynamicDowncast<CSSValueList>(*this)) {
for (auto& listValue : *asList)
listValue->collectDirectComputationalDependencies(values);
return;
}

if (is<CSSPrimitiveValue>(*this))
downcast<CSSPrimitiveValue>(*this).collectDirectComputationalDependencies(values);
}

void CSSValue::collectDirectRootComputationalDependencies(HashSet<CSSPropertyID>& values) const
{
if (auto* asList = dynamicDowncast<CSSValueList>(*this)) {
for (auto& listValue : *asList)
listValue->collectDirectRootComputationalDependencies(values);
return;
}

if (is<CSSPrimitiveValue>(*this))
downcast<CSSPrimitiveValue>(*this).collectDirectRootComputationalDependencies(values);
}
@@ -51,7 +51,7 @@ ExceptionOr<void> DOMCSSRegisterCustomProperty::registerProperty(Document& docum
return Exception { SyntaxError, "Invalid property syntax definition."_s };

RefPtr<CSSCustomPropertyValue> initialValue;
if (!descriptor.initialValue.isEmpty()) {
if (!descriptor.initialValue.isNull()) {
CSSTokenizer tokenizer(descriptor.initialValue);
auto styleResolver = Style::Resolver::create(document);

@@ -60,7 +60,7 @@ ExceptionOr<void> DOMCSSRegisterCustomProperty::registerProperty(Document& docum
auto style = styleResolver->defaultStyleForElement(nullptr);

HashSet<CSSPropertyID> dependencies;
CSSPropertyParser::collectParsedCustomPropertyValueDependencies(*syntax, false, dependencies, tokenizer.tokenRange(), strictCSSParserContext());
CSSPropertyParser::collectParsedCustomPropertyValueDependencies(*syntax, true /* isInitial */, dependencies, tokenizer.tokenRange(), strictCSSParserContext());

if (!dependencies.isEmpty())
return Exception { SyntaxError, "The given initial value must be computationally independent."_s };
@@ -74,12 +74,6 @@ ExceptionOr<void> DOMCSSRegisterCustomProperty::registerProperty(Document& docum

if (!initialValue || !initialValue->isResolved())
return Exception { SyntaxError, "The given initial value does not parse for the given syntax."_s };

initialValue->collectDirectComputationalDependencies(dependencies);
initialValue->collectDirectRootComputationalDependencies(dependencies);

if (!dependencies.isEmpty())
return Exception { SyntaxError, "The given initial value must be computationally independent."_s };
}

CSSRegisteredCustomProperty property { AtomString { descriptor.name }, *syntax, descriptor.inherits, WTFMove(initialValue) };

0 comments on commit f266283

Please sign in to comment.