Title: [278304] trunk
Revision
278304
Author
wei...@apple.com
Date
2021-06-01 06:50:16 -0700 (Tue, 01 Jun 2021)

Log Message

Support calc() on components inside relative color syntax colors
https://bugs.webkit.org/show_bug.cgi?id=226272

Reviewed by Darin Adler.

Source/WebCore:

Added new and updated test cases to fast/css/parsing-relative-color-syntax.html.

Updates support for the CSS Color 5 "Relative Color Syntax" to support
both calc() on components and component permutations within the syntax.

This allows for things like:

    background: lch(from var(--primary-color) 60% calc(c * 0.8) h);

    or

    background: lch(from rebeccapurple g b r);

To make this work, the calc() infrastructure now supports passing a CSSCalcSymbolTable
which allows the logic in the parser to lookup unknown identifiers when parsing a value.
The relative color syntax parsers can then build an appropriate CSSCalcSymbolTable filled
with the components of the origin color.

Since these calc() values are not serialized, this can all happen in the parser, but if
we to be able serialize them in the future, CSSCalcPrimitiveValueNode could be updated
to store the symbol name in addition to storing the value and type (or we could we could
add a new node for it).

* Sources.txt:
* WebCore.xcodeproj/project.pbxproj:
* css/calc/CSSCalcSymbolTable.cpp: Added.
* css/calc/CSSCalcSymbolTable.h: Added.
Add CSSCalcSymbolTable which contains a mapping from CSSValueID to CSSUnitType/double pairs.

* css/calc/CSSCalcExpressionNodeParser.cpp:
(WebCore::CSSCalcExpressionNodeParser::parseValue):
When trying to construct a value node, if the token is a identifier, use the new
symbol table to look up a type/value to use instead. Use a switch to make things
a bit more clear that differentiation is being done based on token type.

* css/calc/CSSCalcExpressionNodeParser.h:
Add reference to a CSSCalcSymbolTable to the parser. The parser should only ever be used
on the stack, so this works well and allows us to avoid copying the table.

* css/calc/CSSCalcValue.cpp:
(WebCore::CSSCalcValue::create):
* css/calc/CSSCalcValue.h:
Pass the CSSCalcSymbolTable to the parser if one is provided. An overload was used
to avoid #including CSSCalcSymbolTable.h in the header just to add a default value.

* css/makevalues.pl:
Give a concrete base of uint16_t to allow it to be forward declared and add DefaultHash
and HashTraits to allow it to be used with HashTable. These match the definition of
CSSPropertyID.

* css/parser/CSSPropertyParserHelpers.cpp:
(WebCore::CSSPropertyParserHelpers::CalcParser::CalcParser):
(WebCore::CSSPropertyParserHelpers::consumeNumberRaw):
(WebCore::CSSPropertyParserHelpers::consumePercentWorkerSafe):
(WebCore::CSSPropertyParserHelpers::consumeAngleRaw):
(WebCore::CSSPropertyParserHelpers::consumeAngleWorkerSafe):
(WebCore::CSSPropertyParserHelpers::consumeAngleOrPercent):
(WebCore::CSSPropertyParserHelpers::consumeOptionalAlphaOrIdent):
(WebCore::CSSPropertyParserHelpers::consumeHueOrIdent):
(WebCore::CSSPropertyParserHelpers::consumeNumberOrIdent):
(WebCore::CSSPropertyParserHelpers::consumePercentOrIdent):
(WebCore::CSSPropertyParserHelpers::consumeNumberOrPercentOrIdentNormalizedForRelativeRGB):
(WebCore::CSSPropertyParserHelpers::parseRelativeRGBParameters):
(WebCore::CSSPropertyParserHelpers::parseRelativeHSLParameters):
(WebCore::CSSPropertyParserHelpers::parseRelativeHWBParameters):
(WebCore::CSSPropertyParserHelpers::parseRelativeLabParameters):
(WebCore::CSSPropertyParserHelpers::parseRelativeLCHParameters):
(WebCore::CSSPropertyParserHelpers::extractChannelValue): Deleted.
(WebCore::CSSPropertyParserHelpers::resolveRelativeColorChannel): Deleted.
Rework relative color syntax parsing to allow permutation of channel symbols
and use of the calc() symbol table support to allow passing in the channels.
This makes the relatative color syntax variants much closer to the normal
variants (with the exception of passing the symbol table) and a subsequent
change will attempt to merge them further.

LayoutTests:

Updated test and results for update support including calc()
and compoment permutation.

* fast/css/parsing-relative-color-syntax-expected.txt:
* fast/css/parsing-relative-color-syntax.html:

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (278303 => 278304)


--- trunk/LayoutTests/ChangeLog	2021-06-01 13:43:06 UTC (rev 278303)
+++ trunk/LayoutTests/ChangeLog	2021-06-01 13:50:16 UTC (rev 278304)
@@ -1,3 +1,16 @@
+2021-06-01  Sam Weinig  <wei...@apple.com>
+
+        Support calc() on components inside relative color syntax colors
+        https://bugs.webkit.org/show_bug.cgi?id=226272
+
+        Reviewed by Darin Adler.
+
+        Updated test and results for update support including calc() 
+        and compoment permutation.
+
+        * fast/css/parsing-relative-color-syntax-expected.txt:
+        * fast/css/parsing-relative-color-syntax.html:
+
 2021-05-31  Diego Pino Garcia  <dp...@igalia.com>
 
         [GLIB] Unreviewed test gardening. Update baselines after r277970.

Modified: trunk/LayoutTests/fast/css/parsing-relative-color-syntax-expected.txt (278303 => 278304)


--- trunk/LayoutTests/fast/css/parsing-relative-color-syntax-expected.txt	2021-06-01 13:43:06 UTC (rev 278303)
+++ trunk/LayoutTests/fast/css/parsing-relative-color-syntax-expected.txt	2021-06-01 13:50:16 UTC (rev 278304)
@@ -42,20 +42,27 @@
 PASS computedStyle("background-color", "rgb(from rgb(10%, 20%, 30%, 40%) 25 g b / 25%)") is "rgba(25, 51, 77, 0.25)"
 PASS computedStyle("background-color", "rgb(from rgb(10%, 20%, 30%, 40%) r 25 b / 25%)") is "rgba(26, 25, 77, 0.25)"
 PASS computedStyle("background-color", "rgb(from rgb(10%, 20%, 30%, 40%) r g 25 / 25%)") is "rgba(26, 51, 25, 0.25)"
-PASS computedStyle("background-color", "rgb(from rebeccapurple g b r)") is "rgba(0, 0, 0, 0)"
-PASS computedStyle("background-color", "rgb(from rebeccapurple b alpha r / g)") is "rgba(0, 0, 0, 0)"
-PASS computedStyle("background-color", "rgb(from rebeccapurple r r r / r)") is "rgba(0, 0, 0, 0)"
-PASS computedStyle("background-color", "rgb(from rebeccapurple alpha alpha alpha / alpha)") is "rgba(0, 0, 0, 0)"
-PASS computedStyle("background-color", "rgb(from rgb(10%, 20%, 30%, 40%) g b r)") is "rgba(0, 0, 0, 0)"
-PASS computedStyle("background-color", "rgb(from rgb(10%, 20%, 30%, 40%) b alpha r / g)") is "rgba(0, 0, 0, 0)"
-PASS computedStyle("background-color", "rgb(from rgb(10%, 20%, 30%, 40%) r r r / r)") is "rgba(0, 0, 0, 0)"
-PASS computedStyle("background-color", "rgb(from rgb(10%, 20%, 30%, 40%) alpha alpha alpha / alpha)") is "rgba(0, 0, 0, 0)"
-PASS computedStyle("background-color", "rgb(from rebeccapurple r 10% 10)") is "rgba(0, 0, 0, 0)"
-PASS computedStyle("background-color", "rgb(from rebeccapurple r 10 10%)") is "rgba(0, 0, 0, 0)"
-PASS computedStyle("background-color", "rgb(from rebeccapurple 0% 10 10)") is "rgba(0, 0, 0, 0)"
-PASS computedStyle("background-color", "rgb(from rgb(10%, 20%, 30%, 40%) r 10% 10)") is "rgba(0, 0, 0, 0)"
-PASS computedStyle("background-color", "rgb(from rgb(10%, 20%, 30%, 40%) r 10 10%)") is "rgba(0, 0, 0, 0)"
-PASS computedStyle("background-color", "rgb(from rgb(10%, 20%, 30%, 40%) 0% 10 10)") is "rgba(0, 0, 0, 0)"
+PASS computedStyle("background-color", "rgb(from rebeccapurple g b r)") is "rgb(51, 153, 102)"
+PASS computedStyle("background-color", "rgb(from rebeccapurple b alpha r / g)") is "rgba(153, 255, 102, 0.2)"
+PASS computedStyle("background-color", "rgb(from rebeccapurple r r r / r)") is "rgba(102, 102, 102, 0.4)"
+PASS computedStyle("background-color", "rgb(from rebeccapurple alpha alpha alpha / alpha)") is "rgb(255, 255, 255)"
+PASS computedStyle("background-color", "rgb(from rgb(10%, 20%, 30%, 40%) g b r)") is "rgb(51, 77, 26)"
+PASS computedStyle("background-color", "rgb(from rgb(10%, 20%, 30%, 40%) b alpha r / g)") is "rgba(77, 102, 26, 0.2)"
+PASS computedStyle("background-color", "rgb(from rgb(10%, 20%, 30%, 40%) r r r / r)") is "rgba(26, 26, 26, 0.1)"
+PASS computedStyle("background-color", "rgb(from rgb(10%, 20%, 30%, 40%) alpha alpha alpha / alpha)") is "rgba(102, 102, 102, 0.4)"
+PASS computedStyle("background-color", "rgb(from rebeccapurple r 10% 10)") is "rgb(102, 26, 10)"
+PASS computedStyle("background-color", "rgb(from rebeccapurple r 10 10%)") is "rgb(102, 10, 26)"
+PASS computedStyle("background-color", "rgb(from rebeccapurple 0% 10 10)") is "rgb(0, 10, 10)"
+PASS computedStyle("background-color", "rgb(from rgb(10%, 20%, 30%, 40%) r 10% 10)") is "rgb(26, 26, 10)"
+PASS computedStyle("background-color", "rgb(from rgb(10%, 20%, 30%, 40%) r 10 10%)") is "rgb(26, 10, 26)"
+PASS computedStyle("background-color", "rgb(from rgb(10%, 20%, 30%, 40%) 0% 10 10)") is "rgb(0, 10, 10)"
+PASS computedStyle("background-color", "rgb(from rebeccapurple calc(r) calc(g) calc(b))") is "rgb(102, 51, 153)"
+PASS computedStyle("background-color", "rgb(from rebeccapurple r calc(g * .5) 10)") is "rgb(102, 26, 10)"
+PASS computedStyle("background-color", "rgb(from rebeccapurple r calc(b * .5) 10)") is "rgb(102, 77, 10)"
+PASS computedStyle("background-color", "rgb(from rebeccapurple r calc(b * .5 + g * .5) 10)") is "rgb(102, 102, 10)"
+PASS computedStyle("background-color", "rgb(from rebeccapurple r calc(b * .5 - g * .5) 10)") is "rgb(102, 51, 10)"
+PASS computedStyle("background-color", "rgb(from rebeccapurple r calc(b * .5 - g * .5) 10)") is "rgb(102, 51, 10)"
+PASS computedStyle("background-color", "rgb(from rgb(10%, 20%, 30%, 40%) calc(r) calc(g) calc(b) / calc(alpha))") is "rgba(26, 51, 77, 0.4)"
 PASS computedStyle("background-color", "rgb(from rebeccapurple red g b)") is "rgba(0, 0, 0, 0)"
 PASS computedStyle("background-color", "rgb(from rebeccapurple l g b)") is "rgba(0, 0, 0, 0)"
 PASS computedStyle("background-color", "rgb(from rebeccapurple h g b)") is "rgba(0, 0, 0, 0)"
@@ -91,14 +98,24 @@
 PASS computedStyle("background-color", "hsl(from rgb(10%, 20%, 30%, 40%) h 25% l / alpha)") is "rgba(39, 51, 64, 0.4)"
 PASS computedStyle("background-color", "hsl(from rgb(10%, 20%, 30%, 40%) h s 25% / alpha)") is "rgba(32, 63, 95, 0.4)"
 PASS computedStyle("background-color", "hsl(from rgb(10%, 20%, 30%, 40%) h s l / .25)") is "rgba(26, 51, 77, 0.25)"
-PASS computedStyle("background-color", "hsl(from rebeccapurple h l s)") is "rgba(0, 0, 0, 0)"
-PASS computedStyle("background-color", "hsl(from rebeccapurple h alpha l / s)") is "rgba(0, 0, 0, 0)"
-PASS computedStyle("background-color", "hsl(from rebeccapurple h l l / l)") is "rgba(0, 0, 0, 0)"
-PASS computedStyle("background-color", "hsl(from rebeccapurple h alpha alpha / alpha)") is "rgba(0, 0, 0, 0)"
-PASS computedStyle("background-color", "hsl(from rgb(10%, 20%, 30%, 40%) h l s)") is "rgba(0, 0, 0, 0)"
-PASS computedStyle("background-color", "hsl(from rgb(10%, 20%, 30%, 40%) h alpha l / s)") is "rgba(0, 0, 0, 0)"
-PASS computedStyle("background-color", "hsl(from rgb(10%, 20%, 30%, 40%) h l l / l)") is "rgba(0, 0, 0, 0)"
-PASS computedStyle("background-color", "hsl(from rgb(10%, 20%, 30%, 40%) h alpha alpha / alpha)") is "rgba(0, 0, 0, 0)"
+PASS computedStyle("background-color", "hsl(from rebeccapurple h l s)") is "rgb(128, 77, 179)"
+PASS computedStyle("background-color", "hsl(from rebeccapurple h alpha l / s)") is "rgba(102, 0, 204, 0.5)"
+PASS computedStyle("background-color", "hsl(from rebeccapurple h l l / l)") is "rgba(102, 61, 143, 0.4)"
+PASS computedStyle("background-color", "hsl(from rebeccapurple h alpha alpha / alpha)") is "rgb(255, 255, 255)"
+PASS computedStyle("background-color", "hsl(from rgb(10%, 20%, 30%, 40%) h l s)") is "rgb(101, 126, 152)"
+PASS computedStyle("background-color", "hsl(from rgb(10%, 20%, 30%, 40%) h alpha l / s)") is "rgba(31, 51, 72, 0.494)"
+PASS computedStyle("background-color", "hsl(from rgb(10%, 20%, 30%, 40%) h l l / l)") is "rgba(41, 51, 62, 0.204)"
+PASS computedStyle("background-color", "hsl(from rgb(10%, 20%, 30%, 40%) h alpha alpha / alpha)") is "rgba(61, 101, 143, 0.4)"
+PASS computedStyle("background-color", "hsl(from rebeccapurple s h l)") is "rgba(0, 0, 0, 0)"
+PASS computedStyle("background-color", "hsl(from rebeccapurple s s s / s)") is "rgba(0, 0, 0, 0)"
+PASS computedStyle("background-color", "hsl(from rebeccapurple h h h / h)") is "rgba(0, 0, 0, 0)"
+PASS computedStyle("background-color", "hsl(from rebeccapurple alpha alpha alpha / alpha)") is "rgba(0, 0, 0, 0)"
+PASS computedStyle("background-color", "hsl(from rgb(10%, 20%, 30%, 40%) s h l)") is "rgba(0, 0, 0, 0)"
+PASS computedStyle("background-color", "hsl(from rgb(10%, 20%, 30%, 40%) s s s / s)") is "rgba(0, 0, 0, 0)"
+PASS computedStyle("background-color", "hsl(from rgb(10%, 20%, 30%, 40%) h h h / h)") is "rgba(0, 0, 0, 0)"
+PASS computedStyle("background-color", "hsl(from rgb(10%, 20%, 30%, 40%) alpha alpha alpha / alpha)") is "rgba(0, 0, 0, 0)"
+PASS computedStyle("background-color", "hsl(from rebeccapurple calc(h) calc(s) calc(l))") is "rgb(102, 51, 153)"
+PASS computedStyle("background-color", "hsl(from rgb(10%, 20%, 30%, 40%) calc(h) calc(s) calc(l) / calc(alpha))") is "rgba(26, 51, 77, 0.4)"
 PASS computedStyle("background-color", "hsl(from rebeccapurple h 10% 10)") is "rgba(0, 0, 0, 0)"
 PASS computedStyle("background-color", "hsl(from rebeccapurple h 10 10%)") is "rgba(0, 0, 0, 0)"
 PASS computedStyle("background-color", "hsl(from rebeccapurple 10% s l)") is "rgba(0, 0, 0, 0)"
@@ -140,14 +157,24 @@
 PASS computedStyle("background-color", "hwb(from rgb(10%, 20%, 30%, 40%) h 25% b / alpha)") is "rgba(64, 70, 77, 0.4)"
 PASS computedStyle("background-color", "hwb(from rgb(10%, 20%, 30%, 40%) h w 25% / alpha)") is "rgba(26, 107, 191, 0.4)"
 PASS computedStyle("background-color", "hwb(from rgb(10%, 20%, 30%, 40%) h w b / .25)") is "rgba(26, 51, 77, 0.25)"
-PASS computedStyle("background-color", "hwb(from rebeccapurple h b w)") is "rgba(0, 0, 0, 0)"
-PASS computedStyle("background-color", "hwb(from rebeccapurple h alpha w / b)") is "rgba(0, 0, 0, 0)"
-PASS computedStyle("background-color", "hwb(from rebeccapurple h w w / w)") is "rgba(0, 0, 0, 0)"
-PASS computedStyle("background-color", "hwb(from rebeccapurple h alpha alpha / alpha)") is "rgba(0, 0, 0, 0)"
-PASS computedStyle("background-color", "hwb(from rgb(10%, 20%, 30%, 40%) h b w)") is "rgba(0, 0, 0, 0)"
-PASS computedStyle("background-color", "hwb(from rgb(10%, 20%, 30%, 40%) h alpha w / b)") is "rgba(0, 0, 0, 0)"
-PASS computedStyle("background-color", "hwb(from rgb(10%, 20%, 30%, 40%) h w w / w)") is "rgba(0, 0, 0, 0)"
-PASS computedStyle("background-color", "hwb(from rgb(10%, 20%, 30%, 40%) h alpha alpha / alpha)") is "rgba(0, 0, 0, 0)"
+PASS computedStyle("background-color", "hwb(from rebeccapurple h b w)") is "rgb(153, 102, 204)"
+PASS computedStyle("background-color", "hwb(from rebeccapurple h alpha w / b)") is "rgba(213, 213, 213, 0.4)"
+PASS computedStyle("background-color", "hwb(from rebeccapurple h w w / w)") is "rgba(128, 51, 204, 0.2)"
+PASS computedStyle("background-color", "hwb(from rebeccapurple h alpha alpha / alpha)") is "rgb(128, 128, 128)"
+PASS computedStyle("background-color", "hwb(from rgb(10%, 20%, 30%, 40%) h b w)") is "rgb(178, 203, 229)"
+PASS computedStyle("background-color", "hwb(from rgb(10%, 20%, 30%, 40%) h alpha w / b)") is "rgba(102, 164, 229, 0.698)"
+PASS computedStyle("background-color", "hwb(from rgb(10%, 20%, 30%, 40%) h w w / w)") is "rgba(26, 126, 229, 0.1)"
+PASS computedStyle("background-color", "hwb(from rgb(10%, 20%, 30%, 40%) h alpha alpha / alpha)") is "rgba(102, 127, 153, 0.4)"
+PASS computedStyle("background-color", "hwb(from rebeccapurple w h b)") is "rgba(0, 0, 0, 0)"
+PASS computedStyle("background-color", "hwb(from rebeccapurple b b b / b)") is "rgba(0, 0, 0, 0)"
+PASS computedStyle("background-color", "hwb(from rebeccapurple h h h / h)") is "rgba(0, 0, 0, 0)"
+PASS computedStyle("background-color", "hwb(from rebeccapurple alpha alpha alpha / alpha)") is "rgba(0, 0, 0, 0)"
+PASS computedStyle("background-color", "hwb(from rgb(10%, 20%, 30%, 40%) w b h)") is "rgba(0, 0, 0, 0)"
+PASS computedStyle("background-color", "hwb(from rgb(10%, 20%, 30%, 40%) b b b / b)") is "rgba(0, 0, 0, 0)"
+PASS computedStyle("background-color", "hwb(from rgb(10%, 20%, 30%, 40%) h h h / h)") is "rgba(0, 0, 0, 0)"
+PASS computedStyle("background-color", "hwb(from rgb(10%, 20%, 30%, 40%) alpha alpha alpha / alpha)") is "rgba(0, 0, 0, 0)"
+PASS computedStyle("background-color", "hwb(from rebeccapurple calc(h) calc(w) calc(b))") is "rgb(102, 51, 153)"
+PASS computedStyle("background-color", "hwb(from rgb(10%, 20%, 30%, 40%) calc(h) calc(w) calc(b) / calc(alpha))") is "rgba(26, 51, 77, 0.4)"
 PASS computedStyle("background-color", "hwb(from rebeccapurple h 10% 10)") is "rgba(0, 0, 0, 0)"
 PASS computedStyle("background-color", "hwb(from rebeccapurple h 10 10%)") is "rgba(0, 0, 0, 0)"
 PASS computedStyle("background-color", "hwb(from rebeccapurple 10% w b)") is "rgba(0, 0, 0, 0)"
@@ -182,14 +209,16 @@
 PASS computedStyle("background-color", "lab(from lab(25% 20 50 / 40%) l 35 b / alpha)") is "lab(25% 35 50 / 0.4)"
 PASS computedStyle("background-color", "lab(from lab(25% 20 50 / 40%) l a 35 / alpha)") is "lab(25% 20 35 / 0.4)"
 PASS computedStyle("background-color", "lab(from lab(25% 20 50 / 40%) l a b / .35)") is "lab(25% 20 50 / 0.35)"
-PASS computedStyle("background-color", "lab(from lab(25% 20 50) l b a)") is "rgba(0, 0, 0, 0)"
+PASS computedStyle("background-color", "lab(from lab(25% 20 50) l b a)") is "lab(25% 50 20)"
+PASS computedStyle("background-color", "lab(from lab(25% 20 50) l a a / a)") is "lab(25% 20 20)"
+PASS computedStyle("background-color", "lab(from lab(25% 20 50 / 40%) l b a)") is "lab(25% 50 20)"
+PASS computedStyle("background-color", "lab(from lab(25% 20 50 / 40%) l a a / a)") is "lab(25% 20 20)"
 PASS computedStyle("background-color", "lab(from lab(25% 20 50) l alpha a / b)") is "rgba(0, 0, 0, 0)"
-PASS computedStyle("background-color", "lab(from lab(25% 20 50) l a a / a)") is "rgba(0, 0, 0, 0)"
 PASS computedStyle("background-color", "lab(from lab(25% 20 50) l alpha alpha / alpha)") is "rgba(0, 0, 0, 0)"
-PASS computedStyle("background-color", "lab(from lab(25% 20 50 / 40%) l b a)") is "rgba(0, 0, 0, 0)"
 PASS computedStyle("background-color", "lab(from lab(25% 20 50 / 40%) l alpha a / b)") is "rgba(0, 0, 0, 0)"
-PASS computedStyle("background-color", "lab(from lab(25% 20 50 / 40%) l a a / a)") is "rgba(0, 0, 0, 0)"
 PASS computedStyle("background-color", "lab(from lab(25% 20 50 / 40%) l alpha alpha / alpha)") is "rgba(0, 0, 0, 0)"
+PASS computedStyle("background-color", "lab(from lab(25% 20 50) calc(l) calc(a) calc(b))") is "lab(25% 20 50)"
+PASS computedStyle("background-color", "lab(from lab(25% 20 50 / 40%) calc(l) calc(a) calc(b) / calc(alpha))") is "lab(25% 20 50 / 0.4)"
 PASS computedStyle("background-color", "lab(from lab(25% 20 50) l 10% 10)") is "rgba(0, 0, 0, 0)"
 PASS computedStyle("background-color", "lab(from lab(25% 20 50) l 10 10%)") is "rgba(0, 0, 0, 0)"
 PASS computedStyle("background-color", "lab(from lab(25% 20 50) 10 a b)") is "rgba(0, 0, 0, 0)"
@@ -231,10 +260,22 @@
 PASS computedStyle("background-color", "lch(from lch(70% 45 30 / 40%) l c 25 / alpha)") is "lch(70% 45 25 / 0.4)"
 PASS computedStyle("background-color", "lch(from lch(70% 45 30 / 40%) l c 25deg / alpha)") is "lch(70% 45 25 / 0.4)"
 PASS computedStyle("background-color", "lch(from lch(70% 45 30 / 40%) l c h / .25)") is "lch(70% 45 30 / 0.25)"
-PASS computedStyle("background-color", "lch(from lch(70% 45 30) alpha c h / l)") is "rgba(0, 0, 0, 0)"
-PASS computedStyle("background-color", "lch(from lch(70% 45 30) alpha c h / alpha)") is "rgba(0, 0, 0, 0)"
-PASS computedStyle("background-color", "lch(from lch(70% 45 30 / 40%) alpha c h / l)") is "rgba(0, 0, 0, 0)"
-PASS computedStyle("background-color", "lch(from lch(70% 45 30 / 40%) alpha c h / alpha)") is "rgba(0, 0, 0, 0)"
+PASS computedStyle("background-color", "lch(from lch(70% 45 30) alpha c h / l)") is "lch(100% 45 30 / 0.7)"
+PASS computedStyle("background-color", "lch(from lch(70% 45 30) l c c / alpha)") is "lch(70% 45 45)"
+PASS computedStyle("background-color", "lch(from lch(70% 45 30) alpha c h / alpha)") is "lch(100% 45 30)"
+PASS computedStyle("background-color", "lch(from lch(70% 45 30) alpha c c / alpha)") is "lch(100% 45 45)"
+PASS computedStyle("background-color", "lch(from lch(70% 45 30 / 40%) alpha c h / l)") is "lch(40% 45 30 / 0.7)"
+PASS computedStyle("background-color", "lch(from lch(70% 45 30 / 40%) l c c / alpha)") is "lch(70% 45 45 / 0.4)"
+PASS computedStyle("background-color", "lch(from lch(70% 45 30 / 40%) alpha c h / alpha)") is "lch(40% 45 30 / 0.4)"
+PASS computedStyle("background-color", "lch(from lch(70% 45 30 / 40%) alpha c c / alpha)") is "lch(40% 45 45 / 0.4)"
+PASS computedStyle("background-color", "lch(from lch(70% 45 30) h l c / alpha)") is "rgba(0, 0, 0, 0)"
+PASS computedStyle("background-color", "lch(from lch(70% 45 30) c c c / c)") is "rgba(0, 0, 0, 0)"
+PASS computedStyle("background-color", "lch(from lch(70% 45 30) alpha alpha alpha / alpha)") is "rgba(0, 0, 0, 0)"
+PASS computedStyle("background-color", "lch(from lch(70% 45 30 / 40%) h l c / alpha)") is "rgba(0, 0, 0, 0)"
+PASS computedStyle("background-color", "lch(from lch(70% 45 30 / 40%) c c c / c)") is "rgba(0, 0, 0, 0)"
+PASS computedStyle("background-color", "lch(from lch(70% 45 30 / 40%) alpha alpha alpha / alpha)") is "rgba(0, 0, 0, 0)"
+PASS computedStyle("background-color", "lch(from lch(70% 45 30) calc(l) calc(c) calc(h))") is "lch(70% 45 30)"
+PASS computedStyle("background-color", "lch(from lch(70% 45 30 / 40%) calc(l) calc(c) calc(h) / calc(alpha))") is "lch(70% 45 30 / 0.4)"
 PASS computedStyle("background-color", "lch(from lch(70% 45 30) l 10% h)") is "rgba(0, 0, 0, 0)"
 PASS computedStyle("background-color", "lch(from lch(70% 45 30) l c 10%)") is "rgba(0, 0, 0, 0)"
 PASS computedStyle("background-color", "lch(from lch(70% 45 30) 10 c h)") is "rgba(0, 0, 0, 0)"
@@ -244,6 +285,21 @@
 PASS computedStyle("background-color", "lch(from lch(70% 45 30) lightness c h)") is "rgba(0, 0, 0, 0)"
 PASS computedStyle("background-color", "lch(from lch(70% 45 30) x c h)") is "rgba(0, 0, 0, 0)"
 PASS computedStyle("background-color", "lch(from lch(70% 45 30) l g b)") is "rgba(0, 0, 0, 0)"
+PASS computedStyle("background-color", "rgb(from var(--bg-color) r g b / 80%)") is "rgba(0, 0, 255, 0.8)"
+PASS computedStyle("background-color", "lch(from var(--color) calc(l / 2) c h)") is "lch(23.138971% 67.989716 134.39125)"
+PASS computedStyle("background-color", "rgb(from var(--color) calc(r * .3 + g * .59 + b * .11) calc(r * .3 + g * .59 + b * .11) calc(r * .3 + g * .59 + b * .11))") is "rgb(76, 76, 76)"
+PASS computedStyle("background-color", "lch(from var(--color) l 0 h)") is "lch(46.277943% 0 134.39125)"
+PASS computedStyle("background-color", "rgb(from indianred 255 g b)") is "rgb(255, 92, 92)"
+PASS computedStyle("background-color", "hsl(from var(--accent) calc(h + 180deg) s l)") is "rgb(178, 32, 40)"
+PASS computedStyle("background-color", "lab(from var(--mycolor) l a b / 100%)") is "lab(62.751923% 52.45802 -34.117283)"
+PASS computedStyle("background-color", "lab(from var(--mycolor) l a b / calc(alpha * 0.8))") is "lab(62.751923% 52.45802 -34.117283 / 0.8)"
+PASS computedStyle("background-color", "lab(from var(--mycolor) l a b / calc(alpha - 20%))") is "lab(62.751923% 52.45802 -34.117283 / 0.8)"
+PASS computedStyle("background-color", "lab(from var(--mycolor) l 0 0)") is "lab(62.751923% 0 0)"
+PASS computedStyle("background-color", "lch(from peru calc(l * 0.8) c h)") is "lch(49.80138% 54.003296 63.680317)"
+PASS computedStyle("background-color", "LCH(from var(--accent) l c calc(h + 180deg))") is "lch(65.49473% 39.446903 10.114471)"
+PASS computedStyle("background-color", "lch(from var(--mycolor) l 0 h)") is "lch(62.751923% 0 326.96112)"
+PASS computedStyle("background-color", "var(--mygray)") is "lch(62.751923% 0 326.96112)"
+PASS computedStyle("background-color", "lch(from var(--mygray) l 30 h)") is "lch(62.751923% 30 326.96112)"
 PASS successfullyParsed is true
 
 TEST COMPLETE

Modified: trunk/LayoutTests/fast/css/parsing-relative-color-syntax.html (278303 => 278304)


--- trunk/LayoutTests/fast/css/parsing-relative-color-syntax.html	2021-06-01 13:43:06 UTC (rev 278303)
+++ trunk/LayoutTests/fast/css/parsing-relative-color-syntax.html	2021-06-01 13:50:16 UTC (rev 278304)
@@ -1,6 +1,15 @@
 <!DOCTYPE html><!-- webkit-test-runner [ CSSRelativeColorSyntaxEnabled=true ] -->
 <html>
     <script src=""
+    <style>
+        html { 
+            --bg-color: blue;
+            --color: green;
+            --accent: lightseagreen;
+            --mycolor: orchid;
+            --mygray: lch(from var(--mycolor) l 0 h);
+        }
+    </style>
 </head>
 <body>
 <script>
@@ -95,24 +104,33 @@
     testComputed(`rgb(from rgb(10%, 20%, 30%, 40%) r 25 b / 25%)`, `rgba(26, 25, 77, 0.25)`);
     testComputed(`rgb(from rgb(10%, 20%, 30%, 40%) r g 25 / 25%)`, `rgba(26, 51, 25, 0.25)`);
 
-    // Testing permutation (unclear if this is allowed per-spec, we disallow it).
-    testComputed(`rgb(from rebeccapurple g b r)`, `rgba(0, 0, 0, 0)`);
-    testComputed(`rgb(from rebeccapurple b alpha r / g)`, `rgba(0, 0, 0, 0)`);
-    testComputed(`rgb(from rebeccapurple r r r / r)`, `rgba(0, 0, 0, 0)`);
-    testComputed(`rgb(from rebeccapurple alpha alpha alpha / alpha)`, `rgba(0, 0, 0, 0)`);
-    testComputed(`rgb(from rgb(10%, 20%, 30%, 40%) g b r)`, `rgba(0, 0, 0, 0)`);
-    testComputed(`rgb(from rgb(10%, 20%, 30%, 40%) b alpha r / g)`, `rgba(0, 0, 0, 0)`);
-    testComputed(`rgb(from rgb(10%, 20%, 30%, 40%) r r r / r)`, `rgba(0, 0, 0, 0)`);
-    testComputed(`rgb(from rgb(10%, 20%, 30%, 40%) alpha alpha alpha / alpha)`, `rgba(0, 0, 0, 0)`);
+    // Testing permutation.
+    testComputed(`rgb(from rebeccapurple g b r)`, `rgb(51, 153, 102)`);
+    testComputed(`rgb(from rebeccapurple b alpha r / g)`, `rgba(153, 255, 102, 0.2)`);
+    testComputed(`rgb(from rebeccapurple r r r / r)`, `rgba(102, 102, 102, 0.4)`);
+    testComputed(`rgb(from rebeccapurple alpha alpha alpha / alpha)`, `rgb(255, 255, 255)`);
+    testComputed(`rgb(from rgb(10%, 20%, 30%, 40%) g b r)`, `rgb(51, 77, 26)`);
+    testComputed(`rgb(from rgb(10%, 20%, 30%, 40%) b alpha r / g)`, `rgba(77, 102, 26, 0.2)`);
+    testComputed(`rgb(from rgb(10%, 20%, 30%, 40%) r r r / r)`, `rgba(26, 26, 26, 0.1)`);
+    testComputed(`rgb(from rgb(10%, 20%, 30%, 40%) alpha alpha alpha / alpha)`, `rgba(102, 102, 102, 0.4)`);
 
-    // Testing invalid mixes of number and percentage
-    testComputed(`rgb(from rebeccapurple r 10% 10)`, `rgba(0, 0, 0, 0)`);
-    testComputed(`rgb(from rebeccapurple r 10 10%)`, `rgba(0, 0, 0, 0)`);
-    testComputed(`rgb(from rebeccapurple 0% 10 10)`, `rgba(0, 0, 0, 0)`);
-    testComputed(`rgb(from rgb(10%, 20%, 30%, 40%) r 10% 10)`, `rgba(0, 0, 0, 0)`);
-    testComputed(`rgb(from rgb(10%, 20%, 30%, 40%) r 10 10%)`, `rgba(0, 0, 0, 0)`);
-    testComputed(`rgb(from rgb(10%, 20%, 30%, 40%) 0% 10 10)`, `rgba(0, 0, 0, 0)`);
+    // Testing mixes of number and percentage. (These would not be allowed in the non-relative syntax).
+    testComputed(`rgb(from rebeccapurple r 10% 10)`, `rgb(102, 26, 10)`);
+    testComputed(`rgb(from rebeccapurple r 10 10%)`, `rgb(102, 10, 26)`);
+    testComputed(`rgb(from rebeccapurple 0% 10 10)`, `rgb(0, 10, 10)`);
+    testComputed(`rgb(from rgb(10%, 20%, 30%, 40%) r 10% 10)`, `rgb(26, 26, 10)`);
+    testComputed(`rgb(from rgb(10%, 20%, 30%, 40%) r 10 10%)`, `rgb(26, 10, 26)`);
+    testComputed(`rgb(from rgb(10%, 20%, 30%, 40%) 0% 10 10)`, `rgb(0, 10, 10)`);
 
+    // Testing with calc().
+    testComputed(`rgb(from rebeccapurple calc(r) calc(g) calc(b))`, `rgb(102, 51, 153)`);
+    testComputed(`rgb(from rebeccapurple r calc(g * .5) 10)`, `rgb(102, 26, 10)`);
+    testComputed(`rgb(from rebeccapurple r calc(b * .5) 10)`, `rgb(102, 77, 10)`);
+    testComputed(`rgb(from rebeccapurple r calc(b * .5 + g * .5) 10)`, `rgb(102, 102, 10)`);
+    testComputed(`rgb(from rebeccapurple r calc(b * .5 - g * .5) 10)`, `rgb(102, 51, 10)`);
+    testComputed(`rgb(from rebeccapurple r calc(b * .5 - g * .5) 10)`, `rgb(102, 51, 10)`);
+    testComputed(`rgb(from rgb(10%, 20%, 30%, 40%) calc(r) calc(g) calc(b) / calc(alpha))`, `rgba(26, 51, 77, 0.4)`);
+
     // Testing invalid component names
     testComputed(`rgb(from rebeccapurple red g b)`, `rgba(0, 0, 0, 0)`);
     testComputed(`rgb(from rebeccapurple l g b)`, `rgba(0, 0, 0, 0)`);
@@ -161,16 +179,30 @@
     testComputed(`hsl(from rgb(10%, 20%, 30%, 40%) h s 25% / alpha)`, `rgba(32, 63, 95, 0.4)`);
     testComputed(`hsl(from rgb(10%, 20%, 30%, 40%) h s l / .25)`, `rgba(26, 51, 77, 0.25)`);
 
-    // Testing permutation (unclear if this is allowed per-spec, we disallow it).
-    testComputed(`hsl(from rebeccapurple h l s)`, `rgba(0, 0, 0, 0)`);
-    testComputed(`hsl(from rebeccapurple h alpha l / s)`, `rgba(0, 0, 0, 0)`);
-    testComputed(`hsl(from rebeccapurple h l l / l)`, `rgba(0, 0, 0, 0)`);
-    testComputed(`hsl(from rebeccapurple h alpha alpha / alpha)`, `rgba(0, 0, 0, 0)`);
-    testComputed(`hsl(from rgb(10%, 20%, 30%, 40%) h l s)`, `rgba(0, 0, 0, 0)`);
-    testComputed(`hsl(from rgb(10%, 20%, 30%, 40%) h alpha l / s)`, `rgba(0, 0, 0, 0)`);
-    testComputed(`hsl(from rgb(10%, 20%, 30%, 40%) h l l / l)`, `rgba(0, 0, 0, 0)`);
-    testComputed(`hsl(from rgb(10%, 20%, 30%, 40%) h alpha alpha / alpha)`, `rgba(0, 0, 0, 0)`);
+    // Testing valid permutation (types match).
+    testComputed(`hsl(from rebeccapurple h l s)`, `rgb(128, 77, 179)`);
+    testComputed(`hsl(from rebeccapurple h alpha l / s)`, `rgba(102, 0, 204, 0.5)`);
+    testComputed(`hsl(from rebeccapurple h l l / l)`, `rgba(102, 61, 143, 0.4)`);
+    testComputed(`hsl(from rebeccapurple h alpha alpha / alpha)`, `rgb(255, 255, 255)`);
+    testComputed(`hsl(from rgb(10%, 20%, 30%, 40%) h l s)`, `rgb(101, 126, 152)`);
+    testComputed(`hsl(from rgb(10%, 20%, 30%, 40%) h alpha l / s)`, `rgba(31, 51, 72, 0.494)`);
+    testComputed(`hsl(from rgb(10%, 20%, 30%, 40%) h l l / l)`, `rgba(41, 51, 62, 0.204)`);
+    testComputed(`hsl(from rgb(10%, 20%, 30%, 40%) h alpha alpha / alpha)`, `rgba(61, 101, 143, 0.4)`);
 
+    // Testing invalid permutation (types don't match).
+    testComputed(`hsl(from rebeccapurple s h l)`, `rgba(0, 0, 0, 0)`);
+    testComputed(`hsl(from rebeccapurple s s s / s)`, `rgba(0, 0, 0, 0)`);
+    testComputed(`hsl(from rebeccapurple h h h / h)`, `rgba(0, 0, 0, 0)`);
+    testComputed(`hsl(from rebeccapurple alpha alpha alpha / alpha)`, `rgba(0, 0, 0, 0)`);
+    testComputed(`hsl(from rgb(10%, 20%, 30%, 40%) s h l)`, `rgba(0, 0, 0, 0)`);
+    testComputed(`hsl(from rgb(10%, 20%, 30%, 40%) s s s / s)`, `rgba(0, 0, 0, 0)`);
+    testComputed(`hsl(from rgb(10%, 20%, 30%, 40%) h h h / h)`, `rgba(0, 0, 0, 0)`);
+    testComputed(`hsl(from rgb(10%, 20%, 30%, 40%) alpha alpha alpha / alpha)`, `rgba(0, 0, 0, 0)`);
+
+    // Testing with calc().
+    testComputed(`hsl(from rebeccapurple calc(h) calc(s) calc(l))`, `rgb(102, 51, 153)`);
+    testComputed(`hsl(from rgb(10%, 20%, 30%, 40%) calc(h) calc(s) calc(l) / calc(alpha))`, `rgba(26, 51, 77, 0.4)`);
+
     // Testing invalid values.
     testComputed(`hsl(from rebeccapurple h 10% 10)`, `rgba(0, 0, 0, 0)`);
     testComputed(`hsl(from rebeccapurple h 10 10%)`, `rgba(0, 0, 0, 0)`);
@@ -228,16 +260,30 @@
     testComputed(`hwb(from rgb(10%, 20%, 30%, 40%) h w 25% / alpha)`, `rgba(26, 107, 191, 0.4)`);
     testComputed(`hwb(from rgb(10%, 20%, 30%, 40%) h w b / .25)`, `rgba(26, 51, 77, 0.25)`);
 
-    // Testing permutation (unclear if this is allowed per-spec, we disallow it).
-    testComputed(`hwb(from rebeccapurple h b w)`, `rgba(0, 0, 0, 0)`);
-    testComputed(`hwb(from rebeccapurple h alpha w / b)`, `rgba(0, 0, 0, 0)`);
-    testComputed(`hwb(from rebeccapurple h w w / w)`, `rgba(0, 0, 0, 0)`);
-    testComputed(`hwb(from rebeccapurple h alpha alpha / alpha)`, `rgba(0, 0, 0, 0)`);
-    testComputed(`hwb(from rgb(10%, 20%, 30%, 40%) h b w)`, `rgba(0, 0, 0, 0)`);
-    testComputed(`hwb(from rgb(10%, 20%, 30%, 40%) h alpha w / b)`, `rgba(0, 0, 0, 0)`);
-    testComputed(`hwb(from rgb(10%, 20%, 30%, 40%) h w w / w)`, `rgba(0, 0, 0, 0)`);
-    testComputed(`hwb(from rgb(10%, 20%, 30%, 40%) h alpha alpha / alpha)`, `rgba(0, 0, 0, 0)`);
+    // Testing valid permutation (types match).
+    testComputed(`hwb(from rebeccapurple h b w)`, `rgb(153, 102, 204)`);
+    testComputed(`hwb(from rebeccapurple h alpha w / b)`, `rgba(213, 213, 213, 0.4)`);
+    testComputed(`hwb(from rebeccapurple h w w / w)`, `rgba(128, 51, 204, 0.2)`);
+    testComputed(`hwb(from rebeccapurple h alpha alpha / alpha)`, `rgb(128, 128, 128)`);
+    testComputed(`hwb(from rgb(10%, 20%, 30%, 40%) h b w)`, `rgb(178, 203, 229)`);
+    testComputed(`hwb(from rgb(10%, 20%, 30%, 40%) h alpha w / b)`, `rgba(102, 164, 229, 0.698)`);
+    testComputed(`hwb(from rgb(10%, 20%, 30%, 40%) h w w / w)`, `rgba(26, 126, 229, 0.1)`);
+    testComputed(`hwb(from rgb(10%, 20%, 30%, 40%) h alpha alpha / alpha)`, `rgba(102, 127, 153, 0.4)`);
 
+    // Testing invalid permutation (types don't match).
+    testComputed(`hwb(from rebeccapurple w h b)`, `rgba(0, 0, 0, 0)`);
+    testComputed(`hwb(from rebeccapurple b b b / b)`, `rgba(0, 0, 0, 0)`);
+    testComputed(`hwb(from rebeccapurple h h h / h)`, `rgba(0, 0, 0, 0)`);
+    testComputed(`hwb(from rebeccapurple alpha alpha alpha / alpha)`, `rgba(0, 0, 0, 0)`);
+    testComputed(`hwb(from rgb(10%, 20%, 30%, 40%) w b h)`, `rgba(0, 0, 0, 0)`);
+    testComputed(`hwb(from rgb(10%, 20%, 30%, 40%) b b b / b)`, `rgba(0, 0, 0, 0)`);
+    testComputed(`hwb(from rgb(10%, 20%, 30%, 40%) h h h / h)`, `rgba(0, 0, 0, 0)`);
+    testComputed(`hwb(from rgb(10%, 20%, 30%, 40%) alpha alpha alpha / alpha)`, `rgba(0, 0, 0, 0)`);
+
+    // Testing with calc().
+    testComputed(`hwb(from rebeccapurple calc(h) calc(w) calc(b))`, `rgb(102, 51, 153)`);
+    testComputed(`hwb(from rgb(10%, 20%, 30%, 40%) calc(h) calc(w) calc(b) / calc(alpha))`, `rgba(26, 51, 77, 0.4)`);
+
     // Testing invalid values.
     testComputed(`hwb(from rebeccapurple h 10% 10)`, `rgba(0, 0, 0, 0)`);
     testComputed(`hwb(from rebeccapurple h 10 10%)`, `rgba(0, 0, 0, 0)`);
@@ -288,16 +334,22 @@
     testComputed(`lab(from lab(25% 20 50 / 40%) l a 35 / alpha)`, `lab(25% 20 35 / 0.4)`);
     testComputed(`lab(from lab(25% 20 50 / 40%) l a b / .35)`, `lab(25% 20 50 / 0.35)`);
 
-    // Testing permutation (unclear if this is allowed per-spec, we disallow it).
-    testComputed(`lab(from lab(25% 20 50) l b a)`, `rgba(0, 0, 0, 0)`);
+    // Testing valid permutation (types match).
+    testComputed(`lab(from lab(25% 20 50) l b a)`, `lab(25% 50 20)`);
+    testComputed(`lab(from lab(25% 20 50) l a a / a)`, `lab(25% 20 20)`);
+    testComputed(`lab(from lab(25% 20 50 / 40%) l b a)`, `lab(25% 50 20)`);
+    testComputed(`lab(from lab(25% 20 50 / 40%) l a a / a)`, `lab(25% 20 20)`);
+
+    // Testing invalid permutation (types don't match).
     testComputed(`lab(from lab(25% 20 50) l alpha a / b)`, `rgba(0, 0, 0, 0)`);
-    testComputed(`lab(from lab(25% 20 50) l a a / a)`, `rgba(0, 0, 0, 0)`);
     testComputed(`lab(from lab(25% 20 50) l alpha alpha / alpha)`, `rgba(0, 0, 0, 0)`);
-    testComputed(`lab(from lab(25% 20 50 / 40%) l b a)`, `rgba(0, 0, 0, 0)`);
     testComputed(`lab(from lab(25% 20 50 / 40%) l alpha a / b)`, `rgba(0, 0, 0, 0)`);
-    testComputed(`lab(from lab(25% 20 50 / 40%) l a a / a)`, `rgba(0, 0, 0, 0)`);
     testComputed(`lab(from lab(25% 20 50 / 40%) l alpha alpha / alpha)`, `rgba(0, 0, 0, 0)`);
 
+    // Testing with calc().
+    testComputed(`lab(from lab(25% 20 50) calc(l) calc(a) calc(b))`, `lab(25% 20 50)`);
+    testComputed(`lab(from lab(25% 20 50 / 40%) calc(l) calc(a) calc(b) / calc(alpha))`, `lab(25% 20 50 / 0.4)`);
+
     // Testing invalid values.
     testComputed(`lab(from lab(25% 20 50) l 10% 10)`, `rgba(0, 0, 0, 0)`);
     testComputed(`lab(from lab(25% 20 50) l 10 10%)`, `rgba(0, 0, 0, 0)`);
@@ -355,12 +407,29 @@
     testComputed(`lch(from lch(70% 45 30 / 40%) l c 25deg / alpha)`, `lch(70% 45 25 / 0.4)`);
     testComputed(`lch(from lch(70% 45 30 / 40%) l c h / .25)`, `lch(70% 45 30 / 0.25)`);
 
-    // Testing permutation (unclear if this is allowed per-spec, we disallow it).
-    testComputed(`lch(from lch(70% 45 30) alpha c h / l)`, `rgba(0, 0, 0, 0)`);
-    testComputed(`lch(from lch(70% 45 30) alpha c h / alpha)`, `rgba(0, 0, 0, 0)`);
-    testComputed(`lch(from lch(70% 45 30 / 40%) alpha c h / l)`, `rgba(0, 0, 0, 0)`);
-    testComputed(`lch(from lch(70% 45 30 / 40%) alpha c h / alpha)`, `rgba(0, 0, 0, 0)`);
+    // Testing valid permutation (types match).
+    // NOTE: 'c' is a vaild hue, as hue is <angle>|<number>.
+    testComputed(`lch(from lch(70% 45 30) alpha c h / l)`, `lch(100% 45 30 / 0.7)`);
+    testComputed(`lch(from lch(70% 45 30) l c c / alpha)`, `lch(70% 45 45)`);
+    testComputed(`lch(from lch(70% 45 30) alpha c h / alpha)`, `lch(100% 45 30)`);
+    testComputed(`lch(from lch(70% 45 30) alpha c c / alpha)`, `lch(100% 45 45)`);
+    testComputed(`lch(from lch(70% 45 30 / 40%) alpha c h / l)`, `lch(40% 45 30 / 0.7)`);
+    testComputed(`lch(from lch(70% 45 30 / 40%) l c c / alpha)`, `lch(70% 45 45 / 0.4)`);
+    testComputed(`lch(from lch(70% 45 30 / 40%) alpha c h / alpha)`, `lch(40% 45 30 / 0.4)`);
+    testComputed(`lch(from lch(70% 45 30 / 40%) alpha c c / alpha)`, `lch(40% 45 45 / 0.4)`);
 
+    // Testing invalid permutation (types don't match).
+    testComputed(`lch(from lch(70% 45 30) h l c / alpha)`, `rgba(0, 0, 0, 0)`);
+    testComputed(`lch(from lch(70% 45 30) c c c / c)`, `rgba(0, 0, 0, 0)`);
+    testComputed(`lch(from lch(70% 45 30) alpha alpha alpha / alpha)`, `rgba(0, 0, 0, 0)`);
+    testComputed(`lch(from lch(70% 45 30 / 40%) h l c / alpha)`, `rgba(0, 0, 0, 0)`);
+    testComputed(`lch(from lch(70% 45 30 / 40%) c c c / c)`, `rgba(0, 0, 0, 0)`);
+    testComputed(`lch(from lch(70% 45 30 / 40%) alpha alpha alpha / alpha)`, `rgba(0, 0, 0, 0)`);
+
+    // Testing with calc().
+    testComputed(`lch(from lch(70% 45 30) calc(l) calc(c) calc(h))`, `lch(70% 45 30)`);
+    testComputed(`lch(from lch(70% 45 30 / 40%) calc(l) calc(c) calc(h) / calc(alpha))`, `lch(70% 45 30 / 0.4)`);
+
     // Testing invalid values.
     testComputed(`lch(from lch(70% 45 30) l 10% h)`, `rgba(0, 0, 0, 0)`);
     testComputed(`lch(from lch(70% 45 30) l c 10%)`, `rgba(0, 0, 0, 0)`);
@@ -373,6 +442,44 @@
     testComputed(`lch(from lch(70% 45 30) lightness c h)`, `rgba(0, 0, 0, 0)`);
     testComputed(`lch(from lch(70% 45 30) x c h)`, `rgba(0, 0, 0, 0)`);
     testComputed(`lch(from lch(70% 45 30) l g b)`, `rgba(0, 0, 0, 0)`);
+
+
+    // Spec examples:
+    // Example 11.
+    testComputed(`rgb(from var(--bg-color) r g b / 80%)`, `rgba(0, 0, 255, 0.8)`);
+    
+    // Example 12.
+    testComputed(`lch(from var(--color) calc(l / 2) c h)`, `lch(23.138971% 67.989716 134.39125)`);
+    
+    // Example 13.
+    testComputed(`rgb(from var(--color) calc(r * .3 + g * .59 + b * .11) calc(r * .3 + g * .59 + b * .11) calc(r * .3 + g * .59 + b * .11))`, `rgb(76, 76, 76)`)
+    testComputed(`lch(from var(--color) l 0 h)`, `lch(46.277943% 0 134.39125)`)
+
+    // Example 14.
+    testComputed(`rgb(from indianred 255 g b)`, `rgb(255, 92, 92)`);
+
+    // Example 15.
+    testComputed(`hsl(from var(--accent) calc(h + 180deg) s l)`, `rgb(178, 32, 40)`);
+
+    // Example 16.
+    testComputed(`lab(from var(--mycolor) l a b / 100%)`, `lab(62.751923% 52.45802 -34.117283)`);
+    testComputed(`lab(from var(--mycolor) l a b / calc(alpha * 0.8))`, `lab(62.751923% 52.45802 -34.117283 / 0.8)`);
+    testComputed(`lab(from var(--mycolor) l a b / calc(alpha - 20%))`, `lab(62.751923% 52.45802 -34.117283 / 0.8)`);
+    
+    // Example 17.
+    testComputed(`lab(from var(--mycolor) l 0 0)`, `lab(62.751923% 0 0)`);
+
+    // Example 18.
+    testComputed(`lch(from peru calc(l * 0.8) c h)`, `lch(49.80138% 54.003296 63.680317)`);
+
+    // Example 19.
+    testComputed(`LCH(from var(--accent) l c calc(h + 180deg))`, `lch(65.49473% 39.446903 10.114471)`);
+
+    // Example 20.
+    testComputed(`lch(from var(--mycolor) l 0 h)`, `lch(62.751923% 0 326.96112)`);
+    testComputed(`var(--mygray)`, `lch(62.751923% 0 326.96112)`);
+    testComputed(`lch(from var(--mygray) l 30 h)`, `lch(62.751923% 30 326.96112)`);
+    
 </script>
     
 <script src=""

Modified: trunk/Source/WebCore/ChangeLog (278303 => 278304)


--- trunk/Source/WebCore/ChangeLog	2021-06-01 13:43:06 UTC (rev 278303)
+++ trunk/Source/WebCore/ChangeLog	2021-06-01 13:50:16 UTC (rev 278304)
@@ -1,3 +1,85 @@
+2021-06-01  Sam Weinig  <wei...@apple.com>
+
+        Support calc() on components inside relative color syntax colors
+        https://bugs.webkit.org/show_bug.cgi?id=226272
+
+        Reviewed by Darin Adler.
+
+        Added new and updated test cases to fast/css/parsing-relative-color-syntax.html.
+
+        Updates support for the CSS Color 5 "Relative Color Syntax" to support
+        both calc() on components and component permutations within the syntax.
+
+        This allows for things like:
+        
+            background: lch(from var(--primary-color) 60% calc(c * 0.8) h);
+
+            or
+
+            background: lch(from rebeccapurple g b r);
+
+        To make this work, the calc() infrastructure now supports passing a CSSCalcSymbolTable
+        which allows the logic in the parser to lookup unknown identifiers when parsing a value.
+        The relative color syntax parsers can then build an appropriate CSSCalcSymbolTable filled
+        with the components of the origin color.
+
+        Since these calc() values are not serialized, this can all happen in the parser, but if
+        we to be able serialize them in the future, CSSCalcPrimitiveValueNode could be updated
+        to store the symbol name in addition to storing the value and type (or we could we could
+        add a new node for it).
+
+        * Sources.txt:
+        * WebCore.xcodeproj/project.pbxproj:
+        * css/calc/CSSCalcSymbolTable.cpp: Added.
+        * css/calc/CSSCalcSymbolTable.h: Added.
+        Add CSSCalcSymbolTable which contains a mapping from CSSValueID to CSSUnitType/double pairs.
+
+        * css/calc/CSSCalcExpressionNodeParser.cpp:
+        (WebCore::CSSCalcExpressionNodeParser::parseValue):
+        When trying to construct a value node, if the token is a identifier, use the new
+        symbol table to look up a type/value to use instead. Use a switch to make things
+        a bit more clear that differentiation is being done based on token type.
+
+        * css/calc/CSSCalcExpressionNodeParser.h:
+        Add reference to a CSSCalcSymbolTable to the parser. The parser should only ever be used
+        on the stack, so this works well and allows us to avoid copying the table.
+
+        * css/calc/CSSCalcValue.cpp:
+        (WebCore::CSSCalcValue::create):
+        * css/calc/CSSCalcValue.h:
+        Pass the CSSCalcSymbolTable to the parser if one is provided. An overload was used
+        to avoid #including CSSCalcSymbolTable.h in the header just to add a default value.
+
+        * css/makevalues.pl:
+        Give a concrete base of uint16_t to allow it to be forward declared and add DefaultHash
+        and HashTraits to allow it to be used with HashTable. These match the definition of
+        CSSPropertyID.
+
+        * css/parser/CSSPropertyParserHelpers.cpp:
+        (WebCore::CSSPropertyParserHelpers::CalcParser::CalcParser):
+        (WebCore::CSSPropertyParserHelpers::consumeNumberRaw):
+        (WebCore::CSSPropertyParserHelpers::consumePercentWorkerSafe):
+        (WebCore::CSSPropertyParserHelpers::consumeAngleRaw):
+        (WebCore::CSSPropertyParserHelpers::consumeAngleWorkerSafe):
+        (WebCore::CSSPropertyParserHelpers::consumeAngleOrPercent):
+        (WebCore::CSSPropertyParserHelpers::consumeOptionalAlphaOrIdent):
+        (WebCore::CSSPropertyParserHelpers::consumeHueOrIdent):
+        (WebCore::CSSPropertyParserHelpers::consumeNumberOrIdent):
+        (WebCore::CSSPropertyParserHelpers::consumePercentOrIdent):
+        (WebCore::CSSPropertyParserHelpers::consumeNumberOrPercentOrIdentNormalizedForRelativeRGB):
+        (WebCore::CSSPropertyParserHelpers::parseRelativeRGBParameters):
+        (WebCore::CSSPropertyParserHelpers::parseRelativeHSLParameters):
+        (WebCore::CSSPropertyParserHelpers::parseRelativeHWBParameters):
+        (WebCore::CSSPropertyParserHelpers::parseRelativeLabParameters):
+        (WebCore::CSSPropertyParserHelpers::parseRelativeLCHParameters):
+        (WebCore::CSSPropertyParserHelpers::extractChannelValue): Deleted.
+        (WebCore::CSSPropertyParserHelpers::resolveRelativeColorChannel): Deleted.
+        Rework relative color syntax parsing to allow permutation of channel symbols
+        and use of the calc() symbol table support to allow passing in the channels.
+        This makes the relatative color syntax variants much closer to the normal
+        variants (with the exception of passing the symbol table) and a subsequent
+        change will attempt to merge them further.
+
 2021-06-01  Alan Bujtas  <za...@apple.com>
 
         [LFC][TFC][Quirks] heightValueOfNearestContainingBlockWithFixedHeight should not need to cross formatting context boundary

Modified: trunk/Source/WebCore/Sources.txt (278303 => 278304)


--- trunk/Source/WebCore/Sources.txt	2021-06-01 13:43:06 UTC (rev 278303)
+++ trunk/Source/WebCore/Sources.txt	2021-06-01 13:50:16 UTC (rev 278304)
@@ -831,6 +831,7 @@
 css/calc/CSSCalcNegateNode.cpp
 css/calc/CSSCalcOperationNode.cpp
 css/calc/CSSCalcPrimitiveValueNode.cpp
+css/calc/CSSCalcSymbolTable.cpp
 css/calc/CSSCalcValue.cpp
 css/parser/CSSAtRuleID.cpp
 css/parser/CSSDeferredParser.cpp

Modified: trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj (278303 => 278304)


--- trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj	2021-06-01 13:43:06 UTC (rev 278303)
+++ trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj	2021-06-01 13:50:16 UTC (rev 278304)
@@ -4079,6 +4079,7 @@
 		BC6932740D7E293900AE44D1 /* JSDOMWindowBase.h in Headers */ = {isa = PBXBuildFile; fileRef = BC6932720D7E293900AE44D1 /* JSDOMWindowBase.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		BC6D44ED0C07F2ED0072D2C9 /* JSHTMLEmbedElement.h in Headers */ = {isa = PBXBuildFile; fileRef = BC6D44EB0C07F2ED0072D2C9 /* JSHTMLEmbedElement.h */; };
 		BC6D6E2609AF943500F59759 /* ScrollView.h in Headers */ = {isa = PBXBuildFile; fileRef = BC6D6E2509AF943500F59759 /* ScrollView.h */; settings = {ATTRIBUTES = (Private, ); }; };
+		BC709DE0266323CF00B9A21C /* CSSCalcSymbolTable.h in Headers */ = {isa = PBXBuildFile; fileRef = BC709DDE266323CE00B9A21C /* CSSCalcSymbolTable.h */; };
 		BC772B3C0C4EA91E0083285F /* CSSHelper.h in Headers */ = {isa = PBXBuildFile; fileRef = BC772B360C4EA91E0083285F /* CSSHelper.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		BC772C470C4EB2C60083285F /* XMLHttpRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = BC772C450C4EB2C60083285F /* XMLHttpRequest.h */; };
 		BC772C4F0C4EB3040083285F /* MIMETypeRegistry.h in Headers */ = {isa = PBXBuildFile; fileRef = BC772C4D0C4EB3040083285F /* MIMETypeRegistry.h */; settings = {ATTRIBUTES = (Private, ); }; };
@@ -14308,6 +14309,8 @@
 		BC6D44EB0C07F2ED0072D2C9 /* JSHTMLEmbedElement.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = JSHTMLEmbedElement.h; sourceTree = "<group>"; };
 		BC6D6E2509AF943500F59759 /* ScrollView.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = ScrollView.h; sourceTree = "<group>"; };
 		BC6EB84526266B61003225A7 /* ColorLuminance.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ColorLuminance.cpp; sourceTree = "<group>"; };
+		BC709DDE266323CE00B9A21C /* CSSCalcSymbolTable.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CSSCalcSymbolTable.h; sourceTree = "<group>"; };
+		BC709DDF266323CE00B9A21C /* CSSCalcSymbolTable.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = CSSCalcSymbolTable.cpp; sourceTree = "<group>"; };
 		BC772B360C4EA91E0083285F /* CSSHelper.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = CSSHelper.h; sourceTree = "<group>"; };
 		BC772C440C4EB2C60083285F /* XMLHttpRequest.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = XMLHttpRequest.cpp; sourceTree = "<group>"; };
 		BC772C450C4EB2C60083285F /* XMLHttpRequest.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = XMLHttpRequest.h; sourceTree = "<group>"; };
@@ -27962,6 +27965,8 @@
 				BCF1645F2662A28D0002F7EF /* CSSCalcOperationNode.h */,
 				BCF164542662A1220002F7EF /* CSSCalcPrimitiveValueNode.cpp */,
 				BCF164532662A1220002F7EF /* CSSCalcPrimitiveValueNode.h */,
+				BC709DDF266323CE00B9A21C /* CSSCalcSymbolTable.cpp */,
+				BC709DDE266323CE00B9A21C /* CSSCalcSymbolTable.h */,
 				49AE2D8C134EE50C0072920A /* CSSCalcValue.cpp */,
 				49AE2D8D134EE50C0072920A /* CSSCalcValue.h */,
 			);
@@ -34676,6 +34681,7 @@
 				26601EBF14B3B9AD0012C0FE /* PlatformEventFactoryIOS.h in Headers */,
 				BCAA487014A052530088FAC4 /* PlatformEventFactoryMac.h in Headers */,
 				A723F77B1484CA4C008C6DBE /* PlatformExportMacros.h in Headers */,
+				BC709DE0266323CF00B9A21C /* CSSCalcSymbolTable.h in Headers */,
 				515BE1951D54F5FB00DD7C68 /* PlatformGamepad.h in Headers */,
 				726D56E2253AE28D0002EF90 /* PlatformImage.h in Headers */,
 				2D7705C7255276CD001D0C94 /* PlatformImageBuffer.h in Headers */,

Modified: trunk/Source/WebCore/css/calc/CSSCalcExpressionNodeParser.cpp (278303 => 278304)


--- trunk/Source/WebCore/css/calc/CSSCalcExpressionNodeParser.cpp	2021-06-01 13:43:06 UTC (rev 278303)
+++ trunk/Source/WebCore/css/calc/CSSCalcExpressionNodeParser.cpp	2021-06-01 13:50:16 UTC (rev 278304)
@@ -31,6 +31,7 @@
 #include "CSSCalcNegateNode.h"
 #include "CSSCalcOperationNode.h"
 #include "CSSCalcPrimitiveValueNode.h"
+#include "CSSCalcSymbolTable.h"
 #include "CSSCalcValue.h"
 #include "CSSParserToken.h"
 #include "CSSParserTokenRange.h"
@@ -180,19 +181,35 @@
 
 bool CSSCalcExpressionNodeParser::parseValue(CSSParserTokenRange& tokens, RefPtr<CSSCalcExpressionNode>& result)
 {
-    // FIXME: Add code here to parse CSSValidID for named constants.
+    auto makeCSSCalcPrimitiveValueNode = [&] (CSSUnitType type, double value) -> bool {
+        if (calcUnitCategory(type) == CalculationCategory::Other)
+            return false;
+        
+        result = CSSCalcPrimitiveValueNode::create(CSSPrimitiveValue::create(value, type));
+        return true;
+    };
 
-    CSSParserToken token = tokens.consumeIncludingWhitespace();
-    if (!(token.type() == NumberToken || token.type() == PercentageToken || token.type() == DimensionToken))
+    auto token = tokens.consumeIncludingWhitespace();
+
+    switch (token.type()) {
+    case IdentToken: {
+        auto value = m_symbolTable.get(token.id());
+        if (!value)
+            return false;
+        return makeCSSCalcPrimitiveValueNode(value->type, value->value);
+    }
+
+    case NumberToken:
+    case PercentageToken:
+    case DimensionToken:
+        return makeCSSCalcPrimitiveValueNode(token.unitType(), token.numericValue());
+
+    default:
         return false;
-    
-    auto type = token.unitType();
-    if (calcUnitCategory(type) == CalculationCategory::Other)
-        return false;
-    
-    result = CSSCalcPrimitiveValueNode::create(CSSPrimitiveValue::create(token.numericValue(), type));
-    
-    return true;
+    }
+
+    ASSERT_NOT_REACHED();
+    return false;
 }
 
 bool CSSCalcExpressionNodeParser::parseCalcValue(CSSParserTokenRange& tokens, int depth, RefPtr<CSSCalcExpressionNode>& result)

Modified: trunk/Source/WebCore/css/calc/CSSCalcExpressionNodeParser.h (278303 => 278304)


--- trunk/Source/WebCore/css/calc/CSSCalcExpressionNodeParser.h	2021-06-01 13:43:06 UTC (rev 278303)
+++ trunk/Source/WebCore/css/calc/CSSCalcExpressionNodeParser.h	2021-06-01 13:50:16 UTC (rev 278304)
@@ -32,13 +32,15 @@
 namespace WebCore {
 
 class CSSCalcExpressionNode;
+class CSSCalcSymbolTable;
 class CSSParserToken;
 class CSSParserTokenRange;
 
 class CSSCalcExpressionNodeParser {
 public:
-    explicit CSSCalcExpressionNodeParser(CalculationCategory destinationCategory)
+    explicit CSSCalcExpressionNodeParser(CalculationCategory destinationCategory, const CSSCalcSymbolTable& symbolTable)
         : m_destinationCategory(destinationCategory)
+        , m_symbolTable(symbolTable)
     {
     }
 
@@ -55,6 +57,7 @@
     bool parseCalcValue(CSSParserTokenRange&, int depth, RefPtr<CSSCalcExpressionNode>&);
 
     CalculationCategory m_destinationCategory;
+    const CSSCalcSymbolTable& m_symbolTable;
 };
 
 }

Copied: trunk/Source/WebCore/css/calc/CSSCalcSymbolTable.cpp (from rev 278303, trunk/Source/WebCore/css/calc/CSSCalcExpressionNodeParser.h) (0 => 278304)


--- trunk/Source/WebCore/css/calc/CSSCalcSymbolTable.cpp	                        (rev 0)
+++ trunk/Source/WebCore/css/calc/CSSCalcSymbolTable.cpp	2021-06-01 13:50:16 UTC (rev 278304)
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2021 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "CSSCalcSymbolTable.h"
+
+#include "CSSUnits.h"
+
+namespace WebCore {
+
+CSSCalcSymbolTable::CSSCalcSymbolTable(std::initializer_list<std::tuple<CSSValueID, CSSUnitType, double>> initializer)
+{
+    for (auto& [identifier, type, value] : initializer)
+        m_table.add(identifier, std::make_pair(type, value));
+}
+
+std::optional<CSSCalcSymbolTable::Value> CSSCalcSymbolTable::get(CSSValueID valueID) const
+{
+    auto it = m_table.find(valueID);
+    if (it == m_table.end())
+        return std::nullopt;
+
+    return {{ it->value.first, it->value.second }};
+}
+
+}

Copied: trunk/Source/WebCore/css/calc/CSSCalcSymbolTable.h (from rev 278303, trunk/Source/WebCore/css/calc/CSSCalcExpressionNodeParser.h) (0 => 278304)


--- trunk/Source/WebCore/css/calc/CSSCalcSymbolTable.h	                        (rev 0)
+++ trunk/Source/WebCore/css/calc/CSSCalcSymbolTable.h	2021-06-01 13:50:16 UTC (rev 278304)
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2021 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include "CSSValueKeywords.h"
+#include <wtf/HashMap.h>
+#include <wtf/Optional.h>
+
+namespace WebCore {
+
+enum class CSSUnitType : uint8_t;
+
+class CSSCalcSymbolTable {
+public:
+    struct Value {
+        CSSUnitType type;
+        double value;
+    };
+
+    CSSCalcSymbolTable() = default;
+    CSSCalcSymbolTable(std::initializer_list<std::tuple<CSSValueID, CSSUnitType, double>>);
+
+    std::optional<Value> get(CSSValueID) const;
+
+private:
+    HashMap<CSSValueID, std::pair<CSSUnitType, double>> m_table;
+};
+
+};

Modified: trunk/Source/WebCore/css/calc/CSSCalcValue.cpp (278303 => 278304)


--- trunk/Source/WebCore/css/calc/CSSCalcValue.cpp	2021-06-01 13:43:06 UTC (rev 278303)
+++ trunk/Source/WebCore/css/calc/CSSCalcValue.cpp	2021-06-01 13:50:16 UTC (rev 278304)
@@ -303,9 +303,9 @@
     ts << ")\n";
 }
 
-RefPtr<CSSCalcValue> CSSCalcValue::create(CSSValueID function, const CSSParserTokenRange& tokens, CalculationCategory destinationCategory, ValueRange range)
+RefPtr<CSSCalcValue> CSSCalcValue::create(CSSValueID function, const CSSParserTokenRange& tokens, CalculationCategory destinationCategory, ValueRange range, const CSSCalcSymbolTable& symbolTable)
 {
-    CSSCalcExpressionNodeParser parser(destinationCategory);
+    CSSCalcExpressionNodeParser parser(destinationCategory, symbolTable);
     auto _expression_ = parser.parseCalc(tokens, function);
     if (!_expression_)
         return nullptr;
@@ -313,7 +313,12 @@
     LOG_WITH_STREAM(Calc, stream << "CSSCalcValue::create " << *result);
     return result;
 }
-    
+
+RefPtr<CSSCalcValue> CSSCalcValue::create(CSSValueID function, const CSSParserTokenRange& tokens, CalculationCategory destinationCategory, ValueRange range)
+{
+    return create(function, tokens, destinationCategory, range, { });
+}
+
 RefPtr<CSSCalcValue> CSSCalcValue::create(const CalculationValue& value, const RenderStyle& style)
 {
     auto _expression_ = createCSS(value._expression_(), style);

Modified: trunk/Source/WebCore/css/calc/CSSCalcValue.h (278303 => 278304)


--- trunk/Source/WebCore/css/calc/CSSCalcValue.h	2021-06-01 13:43:06 UTC (rev 278303)
+++ trunk/Source/WebCore/css/calc/CSSCalcValue.h	2021-06-01 13:50:16 UTC (rev 278304)
@@ -39,6 +39,7 @@
 namespace WebCore {
 
 class CSSCalcExpressionNode;
+class CSSCalcSymbolTable;
 class CSSParserTokenRange;
 class CSSToLengthConversionData;
 class RenderStyle;
@@ -49,6 +50,7 @@
 
 class CSSCalcValue final : public CSSValue {
 public:
+    static RefPtr<CSSCalcValue> create(CSSValueID function, const CSSParserTokenRange&, CalculationCategory destinationCategory, ValueRange, const CSSCalcSymbolTable&);
     static RefPtr<CSSCalcValue> create(CSSValueID function, const CSSParserTokenRange&, CalculationCategory destinationCategory, ValueRange);
     static RefPtr<CSSCalcValue> create(const CalculationValue&, const RenderStyle&);
     ~CSSCalcValue();

Modified: trunk/Source/WebCore/css/makevalues.pl (278303 => 278304)


--- trunk/Source/WebCore/css/makevalues.pl	2021-06-01 13:43:06 UTC (rev 278303)
+++ trunk/Source/WebCore/css/makevalues.pl	2021-06-01 13:50:16 UTC (rev 278304)
@@ -164,6 +164,8 @@
 
 #include <string.h>
 #include <wtf/Forward.h>
+#include <wtf/HashFunctions.h>
+#include <wtf/HashTraits.h>
 
 namespace WebCore {
 
@@ -204,6 +206,12 @@
 }
 
 } // namespace WebCore
+
+namespace WTF {
+template<> struct DefaultHash<WebCore::CSSValueID> : IntHash<unsigned> { };
+template<> struct HashTraits<WebCore::CSSValueID> : StrongEnumHashTraits<WebCore::CSSValueID> { };
+} // namespace WTF
+
 EOF
 close HEADER;
 

Modified: trunk/Source/WebCore/css/parser/CSSPropertyParserHelpers.cpp (278303 => 278304)


--- trunk/Source/WebCore/css/parser/CSSPropertyParserHelpers.cpp	2021-06-01 13:43:06 UTC (rev 278303)
+++ trunk/Source/WebCore/css/parser/CSSPropertyParserHelpers.cpp	2021-06-01 13:50:16 UTC (rev 278304)
@@ -30,6 +30,7 @@
 #include "config.h"
 #include "CSSPropertyParserHelpers.h"
 
+#include "CSSCalcSymbolTable.h"
 #include "CSSCalcValue.h"
 #include "CSSCanvasValue.h"
 #include "CSSCrossfadeValue.h"
@@ -99,7 +100,7 @@
 // FIXME: consider pulling in the parsing logic from CSSCalcExpressionNodeParser.
 class CalcParser {
 public:
-    explicit CalcParser(CSSParserTokenRange& range, CalculationCategory destinationCategory, ValueRange valueRange = ValueRange::All, CSSValuePool& cssValuePool = CSSValuePool::singleton())
+    explicit CalcParser(CSSParserTokenRange& range, CalculationCategory destinationCategory, ValueRange valueRange = ValueRange::All, const CSSCalcSymbolTable& symbolTable = { }, CSSValuePool& cssValuePool = CSSValuePool::singleton())
         : m_sourceRange(range)
         , m_range(range)
         , m_valuePool(cssValuePool)
@@ -107,7 +108,7 @@
         const CSSParserToken& token = range.peek();
         auto functionId = token.functionId();
         if (CSSCalcValue::isCalcFunction(functionId))
-            m_calcValue = CSSCalcValue::create(functionId, consumeFunction(m_range), destinationCategory, valueRange);
+            m_calcValue = CSSCalcValue::create(functionId, consumeFunction(m_range), destinationCategory, valueRange, symbolTable);
     }
 
     const CSSCalcValue* value() const { return m_calcValue.get(); }
@@ -120,6 +121,14 @@
         return m_valuePool.createValue(WTFMove(m_calcValue));
     }
 
+    RefPtr<CSSPrimitiveValue> consumeValueIfCategory(CalculationCategory category)
+    {
+        if (!m_calcValue || m_calcValue->category() != category)
+            return nullptr;
+        m_sourceRange = m_range;
+        return m_valuePool.createValue(WTFMove(m_calcValue));
+    }
+
     RefPtr<CSSPrimitiveValue> consumeInteger(double minimumValue)
     {
         if (!m_calcValue)
@@ -218,11 +227,8 @@
         return std::nullopt;
 
     CalcParser calcParser(range, CalculationCategory::Number);
-    if (const CSSCalcValue* calculation = calcParser.value()) {
-        if (calculation->category() != CalculationCategory::Number)
-            return std::nullopt;
+    if (auto calculation = calcParser.value(); calculation && calculation->category() == CalculationCategory::Number)
         return calcParser.consumeIntegerTypeRaw<IntType>(minimumValue);
-    }
 
     return std::nullopt;
 }
@@ -273,11 +279,7 @@
     const CSSParserToken& token = range.peek();
     if (token.type() == FunctionToken) {
         CalcParser calcParser(range, CalculationCategory::Number, valueRange);
-        if (const auto* calcValue = calcParser.value()) {
-            if (calcValue->category() == CalculationCategory::Number)
-                return calcParser.consumeValue();
-        }
-        return nullptr;
+        return calcParser.consumeValueIfCategory(CalculationCategory::Number);
     }
 
     if (auto number = consumeNumberRaw(range, valueRange))
@@ -399,8 +401,7 @@
     const CSSParserToken& token = range.peek();
     if (token.type() == FunctionToken) {
         CalcParser calcParser(range, CalculationCategory::Length, valueRange);
-        if (calcParser.value() && calcParser.value()->category() == CalculationCategory::Length)
-            return calcParser.consumeValue();
+        return calcParser.consumeValueIfCategory(CalculationCategory::Length);
     }
 
     if (auto result = consumeLengthRaw(range, cssParserMode, valueRange, unitless))
@@ -434,12 +435,8 @@
 {
     const CSSParserToken& token = range.peek();
     if (token.type() == FunctionToken) {
-        CalcParser calcParser(range, CalculationCategory::Percent, valueRange, cssValuePool);
-        if (const CSSCalcValue* calculation = calcParser.value()) {
-            if (calculation->category() == CalculationCategory::Percent)
-                return calcParser.consumeValue();
-        }
-        return nullptr;
+        CalcParser calcParser(range, CalculationCategory::Percent, valueRange, { }, cssValuePool);
+        return calcParser.consumeValueIfCategory(CalculationCategory::Percent);
     }
 
     if (auto percent = consumePercentRaw(range, valueRange))
@@ -480,10 +477,8 @@
         return std::nullopt;
 
     CalcParser calcParser(range, CalculationCategory::Length, valueRange);
-    if (const CSSCalcValue* calculation = calcParser.value()) {
-        if (canConsumeCalcValue(calculation->category(), cssParserMode))
-            return calcParser.consumeLengthOrPercentRaw();
-    }
+    if (auto calculation = calcParser.value(); calculation && canConsumeCalcValue(calculation->category(), cssParserMode))
+        return calcParser.consumeLengthOrPercentRaw();
     return std::nullopt;
 }
 
@@ -492,10 +487,8 @@
     const CSSParserToken& token = range.peek();
     if (token.type() == FunctionToken) {
         CalcParser calcParser(range, CalculationCategory::Length, valueRange);
-        if (const CSSCalcValue* calculation = calcParser.value()) {
-            if (canConsumeCalcValue(calculation->category(), cssParserMode))
-                return calcParser.consumeValue();
-        }
+        if (auto calculation = calcParser.value(); calculation && canConsumeCalcValue(calculation->category(), cssParserMode))
+            return calcParser.consumeValue();
         return nullptr;
     }
 
@@ -544,12 +537,8 @@
 {
     const CSSParserToken& token = range.peek();
     if (token.type() == FunctionToken) {
-        CalcParser calcParser(range, CalculationCategory::Angle, ValueRange::All, cssValuePool);
-        if (const CSSCalcValue* calculation = calcParser.value()) {
-            if (calculation->category() == CalculationCategory::Angle)
-                return calcParser.consumeValue();
-        }
-        return nullptr;
+        CalcParser calcParser(range, CalculationCategory::Angle, ValueRange::All, { }, cssValuePool);
+        return calcParser.consumeValueIfCategory(CalculationCategory::Angle);
     }
 
     if (auto angle = consumeAngleRaw(range, cssParserMode, unitless, unitlessZero))
@@ -561,7 +550,8 @@
 static RefPtr<CSSPrimitiveValue> consumeAngleOrPercent(CSSParserTokenRange& range, CSSParserMode cssParserMode, ValueRange valueRange, UnitlessQuirk unitless, UnitlessZeroQuirk unitlessZero)
 {
     const CSSParserToken& token = range.peek();
-    if (token.type() == DimensionToken) {
+    switch (token.type()) {
+    case DimensionToken:
         switch (token.unitType()) {
         case CSSUnitType::CSS_DEG:
         case CSSUnitType::CSS_RAD:
@@ -569,29 +559,38 @@
         case CSSUnitType::CSS_TURN:
             return CSSValuePool::singleton().createValue(range.consumeIncludingWhitespace().numericValue(), token.unitType());
         default:
-            return nullptr;
+            break;
         }
-    }
-    if (token.type() == NumberToken && shouldAcceptUnitlessValue(token.numericValue(), cssParserMode, unitless, unitlessZero))
-        return CSSValuePool::singleton().createValue(range.consumeIncludingWhitespace().numericValue(), CSSUnitType::CSS_DEG);
+        break;
+    
+    case NumberToken:
+        if (shouldAcceptUnitlessValue(token.numericValue(), cssParserMode, unitless, unitlessZero))
+            return CSSValuePool::singleton().createValue(range.consumeIncludingWhitespace().numericValue(), CSSUnitType::CSS_DEG);
+        break;
 
-    if (token.type() == PercentageToken)
+    case PercentageToken:
         return consumePercent(range, valueRange);
 
-     if (token.type() != FunctionToken)
-         return nullptr;
+    case FunctionToken: {
+        CalcParser angleCalcParser(range, CalculationCategory::Angle, valueRange);
+        if (const CSSCalcValue* calculation = angleCalcParser.value()) {
+            if (calculation->category() == CalculationCategory::Angle)
+                return angleCalcParser.consumeValue();
+        }
 
-    CalcParser angleCalcParser(range, CalculationCategory::Angle, valueRange);
-    if (const CSSCalcValue* calculation = angleCalcParser.value()) {
-        if (calculation->category() == CalculationCategory::Angle)
-            return angleCalcParser.consumeValue();
+        CalcParser percentCalcParser(range, CalculationCategory::Percent, valueRange);
+        if (const CSSCalcValue* calculation = percentCalcParser.value()) {
+            if (calculation->category() == CalculationCategory::Percent)
+                return percentCalcParser.consumeValue();
+        }
+    
+        break;
     }
+    
+    default:
+        break;
+    }
 
-    CalcParser percentCalcParser(range, CalculationCategory::Percent, valueRange);
-    if (const CSSCalcValue* calculation = percentCalcParser.value()) {
-        if (calculation->category() == CalculationCategory::Percent)
-            return percentCalcParser.consumeValue();
-    }
     return nullptr;
 }
 
@@ -614,11 +613,7 @@
         return nullptr;
 
     CalcParser calcParser(range, CalculationCategory::Time, valueRange);
-    if (const CSSCalcValue* calculation = calcParser.value()) {
-        if (calculation->category() == CalculationCategory::Time)
-            return calcParser.consumeValue();
-    }
-    return nullptr;
+    return calcParser.consumeValueIfCategory(CalculationCategory::Time);
 }
 
 RefPtr<CSSPrimitiveValue> consumeResolution(CSSParserTokenRange& range, AllowXResolutionUnit allowX)
@@ -744,19 +739,67 @@
         return 1.0;
 
     if (auto alphaParameter = consumeNumberOrPercentDividedBy100Raw(range))
-        return clampTo(*alphaParameter, 0.0, 1.0);
+        return std::clamp(*alphaParameter, 0.0, 1.0);
 
     return std::nullopt;
 }
 
-template<CSSValueID... allowedIdents> static std::optional<Variant<double, CSSValueID>> consumeOptionalAlphaOrIdent(CSSParserTokenRange& range)
+static std::optional<double> consumeOptionalAlphaOrIdent(CSSParserTokenRange& range, const CSSCalcSymbolTable& symbolTable)
 {
-    if (auto alpha = consumeOptionalAlpha(range))
-        return { *alpha };
+    if (!consumeSlashIncludingWhitespace(range))
+        return 1.0;
 
-    if (auto ident = consumeIdentRaw<allowedIdents...>(range))
-        return { *ident };
+    auto normalizePercent = [](double percent) {
+        return std::clamp(percent / 100.0, 0.0, 1.0);
+    };
 
+    auto normalizeNumber = [](double alpha) {
+        return std::clamp(alpha, 0.0, 1.0);
+    };
+
+    const CSSParserToken& token = range.peek();
+
+    switch (token.type()) {
+    case FunctionToken: {
+        CalcParser percentageCalcParser(range, CalculationCategory::Percent, ValueRange::All, symbolTable);
+        if (auto percent = percentageCalcParser.consumePercentRaw())
+            return normalizePercent(*percent);
+        
+        CalcParser numberCalcParser(range, CalculationCategory::Number, ValueRange::All, symbolTable);
+        if (auto number = numberCalcParser.consumeNumberRaw())
+            return normalizeNumber(*number);
+
+        return std::nullopt;
+    }
+
+    case PercentageToken:
+        if (std::isinf(token.numericValue()))
+            return std::nullopt;
+        return normalizePercent(range.consumeIncludingWhitespace().numericValue());
+
+    case NumberToken:
+        return normalizeNumber(range.consumeIncludingWhitespace().numericValue());
+
+    case IdentToken:
+        if (auto variable = symbolTable.get(range.consumeIncludingWhitespace().id())) {
+            switch (variable->type) {
+            case CSSUnitType::CSS_PERCENTAGE:
+                return normalizePercent(variable->value);
+
+            case CSSUnitType::CSS_NUMBER:
+                return normalizeNumber(variable->value);
+
+            default:
+                return std::nullopt;
+            }
+        }
+        return std::nullopt;
+
+    default:
+        return std::nullopt;
+    }
+
+    ASSERT_NOT_REACHED();
     return std::nullopt;
 }
 
@@ -768,14 +811,59 @@
     return consumeNumberRaw(range);
 }
 
-template<CSSValueID... allowedIdents> static std::optional<Variant<double, CSSValueID>> consumeHueOrIdent(CSSParserTokenRange& range, const CSSParserContext& context)
+static std::optional<double> consumeHueOrIdent(CSSParserTokenRange& range, const CSSCalcSymbolTable& symbolTable)
 {
-    if (auto hue = consumeHue(range, context))
-        return { *hue };
+    const CSSParserToken& token = range.peek();
 
-    if (auto ident = consumeIdentRaw<allowedIdents...>(range))
-        return { *ident };
+    switch (token.type()) {
+    case FunctionToken: {
+        CalcParser angleCalcParser(range, CalculationCategory::Angle, ValueRange::All, symbolTable);
+        if (auto angle = angleCalcParser.consumeAngleRaw())
+            return CSSPrimitiveValue::computeDegrees(angle->type, angle->value);
+        
+        CalcParser numberCalcParser(range, CalculationCategory::Number, ValueRange::All, symbolTable);
+        return numberCalcParser.consumeNumberRaw();
+    }
+    
+    case DimensionToken: {
+        auto unitType = token.unitType();
+        switch (unitType) {
+        case CSSUnitType::CSS_DEG:
+        case CSSUnitType::CSS_RAD:
+        case CSSUnitType::CSS_GRAD:
+        case CSSUnitType::CSS_TURN:
+            return CSSPrimitiveValue::computeDegrees(unitType, range.consumeIncludingWhitespace().numericValue());
+        default:
+            return std::nullopt;
+        }
+    }
+    
+    case NumberToken:
+        return range.consumeIncludingWhitespace().numericValue();
 
+    case IdentToken:
+        if (auto variable = symbolTable.get(range.consumeIncludingWhitespace().id())) {
+            switch (variable->type) {
+            case CSSUnitType::CSS_DEG:
+            case CSSUnitType::CSS_RAD:
+            case CSSUnitType::CSS_GRAD:
+            case CSSUnitType::CSS_TURN:
+                return CSSPrimitiveValue::computeDegrees(variable->type, variable->value);
+
+            case CSSUnitType::CSS_NUMBER:
+                return variable->value;
+
+            default:
+                return std::nullopt;
+            }
+        }
+        return std::nullopt;
+
+    default:
+        return std::nullopt;
+    }
+
+    ASSERT_NOT_REACHED();
     return std::nullopt;
 }
 
@@ -784,66 +872,132 @@
     return std::fmod(std::fmod(hue, 360.0) + 360.0, 360.0);
 }
 
-template<CSSValueID... allowedIdents> static std::optional<Variant<double, CSSValueID>> consumeNumberOrIdent(CSSParserTokenRange& range)
+static std::optional<double> consumeNumberOrIdent(CSSParserTokenRange& range, const CSSCalcSymbolTable& symbolTable)
 {
-    if (auto number = consumeNumberRaw(range))
-        return { *number };
+    const CSSParserToken& token = range.peek();
 
-    if (auto ident = consumeIdentRaw<allowedIdents...>(range))
-        return { *ident };
+    switch (token.type()) {
+    case FunctionToken: {
+        CalcParser calcParser(range, CalculationCategory::Number, ValueRange::All, symbolTable);
+        return calcParser.consumeNumberRaw();
+    }
 
+    case NumberToken:
+        return range.consumeIncludingWhitespace().numericValue();
+
+    case IdentToken:
+        if (auto variable = symbolTable.get(range.consumeIncludingWhitespace().id())) {
+            switch (variable->type) {
+            case CSSUnitType::CSS_NUMBER:
+                return variable->value;
+
+            default:
+                return std::nullopt;
+            }
+        }
+        return std::nullopt;
+
+    default:
+        return std::nullopt;
+    }
+
+    ASSERT_NOT_REACHED();
     return std::nullopt;
 }
 
-template<CSSValueID... allowedIdents> static std::optional<Variant<double, CSSValueID>> consumePercentOrIdent(CSSParserTokenRange& range)
+static std::optional<double> consumePercentOrIdent(CSSParserTokenRange& range, const CSSCalcSymbolTable& symbolTable)
 {
-    if (auto percent = consumePercentRaw(range))
-        return { *percent };
+    const CSSParserToken& token = range.peek();
 
-    if (auto ident = consumeIdentRaw<allowedIdents...>(range))
-        return { *ident };
+    switch (token.type()) {
+    case FunctionToken: {
+        CalcParser calcParser(range, CalculationCategory::Percent, ValueRange::All, symbolTable);
+        return calcParser.consumePercentRaw();
+    }
 
+    case PercentageToken:
+        if (std::isinf(token.numericValue()))
+            return std::nullopt;
+        return range.consumeIncludingWhitespace().numericValue();
+
+    case IdentToken:
+        if (auto variable = symbolTable.get(range.consumeIncludingWhitespace().id())) {
+            switch (variable->type) {
+            case CSSUnitType::CSS_PERCENTAGE:
+                return variable->value;
+
+            default:
+                return std::nullopt;
+            }
+        }
+        return std::nullopt;
+
+    default:
+        return std::nullopt;
+    }
+
+    ASSERT_NOT_REACHED();
     return std::nullopt;
 }
 
-template<CSSValueID C1, CSSValueID C2, CSSValueID C3, CSSValueID AlphaChannel, typename ColorType> static auto extractChannelValue(CSSValueID channel, const ColorType& originColor) -> typename ColorType::ComponentType
+enum class RGBComponentType { Number, Percentage };
+
+static std::optional<uint8_t> consumeNumberOrPercentOrIdentNormalizedForRelativeRGB(CSSParserTokenRange& range, const CSSCalcSymbolTable& symbolTable)
 {
-    auto components = asColorComponents(originColor);
-    switch (channel) {
-    case C1:
-        return components[0];
-    case C2:
-        return components[1];
-    case C3:
-        return components[2];
-    case AlphaChannel:
-        return components[3];
-    default:
-        ASSERT_NOT_REACHED();
+    auto normalizePercent = [](double percent) {
+        return convertPrescaledSRGBAFloatToSRGBAByte(percent / 100.0 * 255.0);
+    };
+
+    auto normalizeNumber = [](double number) {
+        return convertPrescaledSRGBAFloatToSRGBAByte(number);
+    };
+
+    const CSSParserToken& token = range.peek();
+
+    switch (token.type()) {
+    case FunctionToken: {
+        CalcParser percentageCalcParser(range, CalculationCategory::Percent, ValueRange::All, symbolTable);
+        if (auto percent = percentageCalcParser.consumePercentRaw())
+            return normalizePercent(*percent);
+        
+        CalcParser numberCalcParser(range, CalculationCategory::Number, ValueRange::All, symbolTable);
+        if (auto number = numberCalcParser.consumeNumberRaw())
+            return normalizeNumber(*number);
+        
+        return std::nullopt;
     }
 
-    return 0;
-}
+    case PercentageToken:
+        if (std::isinf(token.numericValue()))
+            return std::nullopt;
+        return normalizePercent(range.consumeIncludingWhitespace().numericValue());
 
-template<CSSValueID C1, CSSValueID C2, CSSValueID C3, CSSValueID AlphaChannel, typename ColorType, typename ValueTransformer> static decltype(auto) resolveRelativeColorChannel(const Variant<double, CSSValueID>& parsedChannel, const ColorType& originColor, ValueTransformer&& valueTransformer)
-{
-    return switchOn(parsedChannel,
-        [&] (double value) {
-            return valueTransformer(value);
-        },
-        [&] (CSSValueID channel) {
-            return extractChannelValue<C1, C2, C3, AlphaChannel>(channel, originColor);
+    case NumberToken:
+        return normalizeNumber(range.consumeIncludingWhitespace().numericValue());
+
+    case IdentToken:
+        if (auto variable = symbolTable.get(range.consumeIncludingWhitespace().id())) {
+            switch (variable->type) {
+            case CSSUnitType::CSS_PERCENTAGE:
+                return normalizePercent(variable->value);
+
+            case CSSUnitType::CSS_NUMBER:
+                return normalizeNumber(variable->value);
+
+            default:
+                return std::nullopt;
+            }
         }
-    );
-}
+        return std::nullopt;
 
-template<CSSValueID C1, CSSValueID C2, CSSValueID C3, CSSValueID AlphaChannel, typename ColorType> static decltype(auto) resolveRelativeColorChannel(const Variant<double, CSSValueID>& parsedChannel, const ColorType& originColor)
-{
-    return resolveRelativeColorChannel<C1, C2, C3, AlphaChannel>(parsedChannel, originColor, [](auto value) { return value; });
+    default:
+        return std::nullopt;
+    }
+
+    ASSERT_NOT_REACHED();
+    return std::nullopt;
 }
 
-enum class RGBComponentType { Number, Percentage };
-
 static uint8_t clampRGBComponent(double value, RGBComponentType componentType)
 {
     if (componentType == RGBComponentType::Percentage)
@@ -922,26 +1076,28 @@
     if (!originColor.isValid())
         return { };
 
-    std::optional<RGBComponentType> componentType;
-    auto redResult = consumeRelativeRGBComponent<CSSValueR>(args, componentType);
-    if (!redResult)
+    auto originColorAsSRGB = originColor.toColorTypeLossy<SRGBA<float>>();
+
+    CSSCalcSymbolTable symbolTable {
+        { CSSValueR, CSSUnitType::CSS_PERCENTAGE, originColorAsSRGB.red * 100.0 },
+        { CSSValueG, CSSUnitType::CSS_PERCENTAGE, originColorAsSRGB.green * 100.0 },
+        { CSSValueB, CSSUnitType::CSS_PERCENTAGE, originColorAsSRGB.blue * 100.0 },
+        { CSSValueAlpha, CSSUnitType::CSS_PERCENTAGE, originColorAsSRGB.alpha * 100.0 }
+    };
+
+    auto red = consumeNumberOrPercentOrIdentNormalizedForRelativeRGB(args, symbolTable);
+    if (!red)
         return { };
-    auto red = redResult->value;
-    componentType = redResult->type;
 
-    auto greenResult = consumeRelativeRGBComponent<CSSValueG>(args, componentType);
-    if (!greenResult)
+    auto green = consumeNumberOrPercentOrIdentNormalizedForRelativeRGB(args, symbolTable);
+    if (!green)
         return { };
-    auto green = greenResult->value;
-    componentType = greenResult->type;
 
-    auto blueResult = consumeRelativeRGBComponent<CSSValueB>(args, componentType);
-    if (!blueResult)
+    auto blue = consumeNumberOrPercentOrIdentNormalizedForRelativeRGB(args, symbolTable);
+    if (!blue)
         return { };
-    auto blue = blueResult->value;
-    componentType = blueResult->type;
 
-    auto alpha = consumeOptionalAlphaOrIdent<CSSValueAlpha>(args);
+    auto alpha = consumeOptionalAlphaOrIdent(args, symbolTable);
     if (!alpha)
         return { };
 
@@ -948,26 +1104,9 @@
     if (!args.atEnd())
         return { };
 
-    // After parsing, convert identifiers to values from the origin color.
+    auto normalizedAlpha = convertFloatAlphaTo<uint8_t>(*alpha);
 
-    // FIXME: Do we want to being doing this in uint8_t values? Or should we use 
-    // higher precision and clamp at the end? It won't make a difference until we
-    // support calculations on the origin's components.
-    auto originColorAsSRGB = originColor.toSRGBALossy<uint8_t>();
-
-    auto resolvedComponentType = componentType.value_or(RGBComponentType::Percentage);
-    auto channelResolver = [resolvedComponentType](auto value) {
-        return clampRGBComponent(value, resolvedComponentType);
-    };
-
-    auto resolvedRed = resolveRelativeColorChannel<CSSValueR, CSSValueG, CSSValueB, CSSValueAlpha>(red, originColorAsSRGB, channelResolver);
-    auto resolvedGreen = resolveRelativeColorChannel<CSSValueR, CSSValueG, CSSValueB, CSSValueAlpha>(green, originColorAsSRGB, channelResolver);
-    auto resolvedBlue = resolveRelativeColorChannel<CSSValueR, CSSValueG, CSSValueB, CSSValueAlpha>(blue, originColorAsSRGB, channelResolver);
-    auto resolvedAlpha = resolveRelativeColorChannel<CSSValueR, CSSValueG, CSSValueB, CSSValueAlpha>(*alpha, originColorAsSRGB, [](auto value) {
-        return convertFloatAlphaTo<uint8_t>(value);
-    });
-
-    return SRGBA<uint8_t> { resolvedRed, resolvedGreen, resolvedBlue, resolvedAlpha };
+    return SRGBA<uint8_t> { *red, *green, *blue, normalizedAlpha };
 }
 
 enum class RGBFunctionMode { RGB, RGBA };
@@ -1049,19 +1188,28 @@
     if (!originColor.isValid())
         return { };
 
-    auto hue = consumeHueOrIdent<CSSValueH>(args, context);
+    auto originColorAsHSL = originColor.toColorTypeLossy<HSLA<float>>();
+
+    CSSCalcSymbolTable symbolTable {
+        { CSSValueH, CSSUnitType::CSS_DEG, originColorAsHSL.hue },
+        { CSSValueS, CSSUnitType::CSS_PERCENTAGE, originColorAsHSL.saturation },
+        { CSSValueL, CSSUnitType::CSS_PERCENTAGE, originColorAsHSL.lightness },
+        { CSSValueAlpha, CSSUnitType::CSS_PERCENTAGE, originColorAsHSL.alpha * 100.0 }
+    };
+
+    auto hue = consumeHueOrIdent(args, symbolTable);
     if (!hue)
         return { };
 
-    auto saturation = consumePercentOrIdent<CSSValueS>(args);
+    auto saturation = consumePercentOrIdent(args, symbolTable);
     if (!saturation)
         return { };
 
-    auto lightness = consumePercentOrIdent<CSSValueL>(args);
+    auto lightness = consumePercentOrIdent(args, symbolTable);
     if (!lightness)
         return { };
 
-    auto alpha = consumeOptionalAlphaOrIdent<CSSValueAlpha>(args);
+    auto alpha = consumeOptionalAlphaOrIdent(args, symbolTable);
     if (!alpha)
         return { };
 
@@ -1068,22 +1216,12 @@
     if (!args.atEnd())
         return { };
 
-    // After parsing, convert identifiers to values from the origin color.
+    auto normalizedHue = normalizeHue(*hue);
+    auto normalizedSaturation = std::clamp(*saturation, 0.0, 100.0);
+    auto normalizedLightness = std::clamp(*lightness, 0.0, 100.0);
+    auto normalizedAlpha = std::clamp(*alpha, 0.0, 1.0);
 
-    auto originColorAsHSL = originColor.toColorTypeLossy<HSLA<float>>();
-
-    auto resolvedHue = resolveRelativeColorChannel<CSSValueH, CSSValueS, CSSValueL, CSSValueAlpha>(*hue, originColorAsHSL, [](auto hue) {
-        return normalizeHue(hue);
-    });
-    auto resolvedSaturation = resolveRelativeColorChannel<CSSValueH, CSSValueS, CSSValueL, CSSValueAlpha>(*saturation, originColorAsHSL, [](auto saturation) {
-        return clampTo(saturation, 0.0, 100.0);
-    });
-    auto resolvedLightness = resolveRelativeColorChannel<CSSValueH, CSSValueS, CSSValueL, CSSValueAlpha>(*lightness, originColorAsHSL, [](auto lightness) {
-        return clampTo(lightness, 0.0, 100.0);
-    });
-    auto resolvedAlpha = resolveRelativeColorChannel<CSSValueH, CSSValueS, CSSValueL, CSSValueAlpha>(*alpha, originColorAsHSL);
-
-    return convertColor<SRGBA<uint8_t>>(HSLA<float> { static_cast<float>(resolvedHue), static_cast<float>(resolvedSaturation), static_cast<float>(resolvedLightness), static_cast<float>(resolvedAlpha) });
+    return convertColor<SRGBA<uint8_t>>(HSLA<float> { static_cast<float>(normalizedHue), static_cast<float>(normalizedSaturation), static_cast<float>(normalizedLightness), static_cast<float>(normalizedAlpha) });
 }
 
 enum class HSLFunctionMode { HSL, HSLA };
@@ -1123,15 +1261,14 @@
         return { };
 
     auto normalizedHue = normalizeHue(*hue);
-    auto normalizedSaturation = clampTo(*saturation, 0.0, 100.0);
-    auto normalizedLightness = clampTo(*lightness, 0.0, 100.0);
-    auto normalizedAlpha = clampTo(*alpha, 0.0, 1.0);
+    auto normalizedSaturation = std::clamp(*saturation, 0.0, 100.0);
+    auto normalizedLightness = std::clamp(*lightness, 0.0, 100.0);
+    auto normalizedAlpha = std::clamp(*alpha, 0.0, 1.0);
 
     return convertColor<SRGBA<uint8_t>>(HSLA<float> { static_cast<float>(normalizedHue), static_cast<float>(normalizedSaturation), static_cast<float>(normalizedLightness), static_cast<float>(normalizedAlpha) });
 }
 
-template<typename ComponentType>
-struct WhitenessBlackness {
+template<typename ComponentType> struct WhitenessBlackness {
     ComponentType whiteness;
     ComponentType blackness;
 };
@@ -1165,19 +1302,28 @@
     if (!originColor.isValid())
         return { };
 
-    auto hue = consumeHueOrIdent<CSSValueH>(args, context);
+    auto originColorAsHWB = originColor.toColorTypeLossy<HWBA<float>>();
+
+    CSSCalcSymbolTable symbolTable {
+        { CSSValueH, CSSUnitType::CSS_DEG, originColorAsHWB.hue },
+        { CSSValueW, CSSUnitType::CSS_PERCENTAGE, originColorAsHWB.whiteness },
+        { CSSValueB, CSSUnitType::CSS_PERCENTAGE, originColorAsHWB.blackness },
+        { CSSValueAlpha, CSSUnitType::CSS_PERCENTAGE, originColorAsHWB.alpha * 100.0 }
+    };
+
+    auto hue = consumeHueOrIdent(args, symbolTable);
     if (!hue)
         return { };
 
-    auto whiteness = consumePercentOrIdent<CSSValueW>(args);
+    auto whiteness = consumePercentOrIdent(args, symbolTable);
     if (!whiteness)
         return { };
 
-    auto blackness = consumePercentOrIdent<CSSValueB>(args);
+    auto blackness = consumePercentOrIdent(args, symbolTable);
     if (!blackness)
         return { };
 
-    auto alpha = consumeOptionalAlphaOrIdent<CSSValueAlpha>(args);
+    auto alpha = consumeOptionalAlphaOrIdent(args, symbolTable);
     if (!alpha)
         return { };
 
@@ -1184,20 +1330,10 @@
     if (!args.atEnd())
         return { };
 
-    // After parsing, convert identifiers to values from the origin color.
+    auto normalizedHue = normalizeHue(*hue);
+    auto [normalizedWhitness, normalizedBlackness] = normalizeWhitenessBlackness(*whiteness, *blackness);
 
-    auto originColorAsHWB = originColor.toColorTypeLossy<HWBA<float>>();
-
-    auto resolvedHue = resolveRelativeColorChannel<CSSValueH, CSSValueW, CSSValueB, CSSValueAlpha>(*hue, originColorAsHWB, [](auto hue) {
-        return normalizeHue(hue);
-    });
-    auto resolvedWhiteness = resolveRelativeColorChannel<CSSValueH, CSSValueW, CSSValueB, CSSValueAlpha>(*whiteness, originColorAsHWB);
-    auto resolvedBlackness = resolveRelativeColorChannel<CSSValueH, CSSValueW, CSSValueB, CSSValueAlpha>(*blackness, originColorAsHWB);
-    auto resolvedAlpha = resolveRelativeColorChannel<CSSValueH, CSSValueW, CSSValueB, CSSValueAlpha>(*alpha, originColorAsHWB);
-
-    auto [normalizedWhitness, normalizedBlackness] = normalizeWhitenessBlackness(resolvedWhiteness, resolvedBlackness);
-
-    return convertColor<SRGBA<uint8_t>>(HWBA<float> { static_cast<float>(resolvedHue), static_cast<float>(normalizedWhitness), static_cast<float>(normalizedBlackness), static_cast<float>(resolvedAlpha) });
+    return convertColor<SRGBA<uint8_t>>(HWBA<float> { static_cast<float>(normalizedHue), static_cast<float>(normalizedWhitness), static_cast<float>(normalizedBlackness), static_cast<float>(*alpha) });
 }
 
 static Color parseHWBParameters(CSSParserTokenRange& range, const CSSParserContext& context)
@@ -1246,19 +1382,28 @@
     if (!originColor.isValid())
         return { };
 
-    auto lightness = consumePercentOrIdent<CSSValueL>(args);
+    auto originColorAsLab = originColor.toColorTypeLossy<Lab<float>>();
+
+    CSSCalcSymbolTable symbolTable {
+        { CSSValueL, CSSUnitType::CSS_PERCENTAGE, originColorAsLab.lightness },
+        { CSSValueA, CSSUnitType::CSS_NUMBER, originColorAsLab.a },
+        { CSSValueB, CSSUnitType::CSS_NUMBER, originColorAsLab.b },
+        { CSSValueAlpha, CSSUnitType::CSS_PERCENTAGE, originColorAsLab.alpha * 100.0 }
+    };
+
+    auto lightness = consumePercentOrIdent(args, symbolTable);
     if (!lightness)
         return { };
 
-    auto aValue = consumeNumberOrIdent<CSSValueA>(args);
+    auto aValue = consumeNumberOrIdent(args, symbolTable);
     if (!aValue)
         return { };
 
-    auto bValue = consumeNumberOrIdent<CSSValueB>(args);
+    auto bValue = consumeNumberOrIdent(args, symbolTable);
     if (!bValue)
         return { };
 
-    auto alpha = consumeOptionalAlphaOrIdent<CSSValueAlpha>(args);
+    auto alpha = consumeOptionalAlphaOrIdent(args, symbolTable);
     if (!alpha)
         return { };
 
@@ -1265,18 +1410,9 @@
     if (!args.atEnd())
         return { };
 
-    // After parsing, convert identifiers to values from the origin color.
+    auto normalizedLightness = std::max(0.0, *lightness);
 
-    auto originColorAsLab = originColor.toColorTypeLossy<Lab<float>>();
-
-    auto resolvedLightness = resolveRelativeColorChannel<CSSValueL, CSSValueA, CSSValueB, CSSValueAlpha>(*lightness, originColorAsLab, [](auto lightness) {
-        return std::max(0.0, lightness);
-    });
-    auto resolvedAValue = resolveRelativeColorChannel<CSSValueL, CSSValueA, CSSValueB, CSSValueAlpha>(*aValue, originColorAsLab);
-    auto resolvedBValue = resolveRelativeColorChannel<CSSValueL, CSSValueA, CSSValueB, CSSValueAlpha>(*bValue, originColorAsLab);
-    auto resolvedAlpha = resolveRelativeColorChannel<CSSValueL, CSSValueA, CSSValueB, CSSValueAlpha>(*alpha, originColorAsLab);
-
-    return Lab<float> { static_cast<float>(resolvedLightness), static_cast<float>(resolvedAValue), static_cast<float>(resolvedBValue), static_cast<float>(resolvedAlpha) };
+    return Lab<float> { static_cast<float>(normalizedLightness), static_cast<float>(*aValue), static_cast<float>(*bValue), static_cast<float>(*alpha) };
 }
 
 static Color parseLabParameters(CSSParserTokenRange& range, const CSSParserContext& context)
@@ -1324,19 +1460,28 @@
     if (!originColor.isValid())
         return { };
 
-    auto lightness = consumePercentOrIdent<CSSValueL>(args);
+    auto originColorAsLCH = originColor.toColorTypeLossy<LCHA<float>>();
+
+    CSSCalcSymbolTable symbolTable {
+        { CSSValueL, CSSUnitType::CSS_PERCENTAGE, originColorAsLCH.lightness },
+        { CSSValueC, CSSUnitType::CSS_NUMBER, originColorAsLCH.chroma },
+        { CSSValueH, CSSUnitType::CSS_DEG, originColorAsLCH.hue },
+        { CSSValueAlpha, CSSUnitType::CSS_PERCENTAGE, originColorAsLCH.alpha * 100.0 }
+    };
+
+    auto lightness = consumePercentOrIdent(args, symbolTable);
     if (!lightness)
         return { };
 
-    auto chroma = consumeNumberOrIdent<CSSValueC>(args);
+    auto chroma = consumeNumberOrIdent(args, symbolTable);
     if (!chroma)
         return { };
 
-    auto hue = consumeHueOrIdent<CSSValueH>(args, context);
+    auto hue = consumeHueOrIdent(args, symbolTable);
     if (!hue)
         return { };
 
-    auto alpha = consumeOptionalAlphaOrIdent<CSSValueAlpha>(args);
+    auto alpha = consumeOptionalAlphaOrIdent(args, symbolTable);
     if (!alpha)
         return { };
 
@@ -1343,20 +1488,11 @@
     if (!args.atEnd())
         return { };
 
-    auto originColorAsLCH = originColor.toColorTypeLossy<LCHA<float>>();
+    auto normalizedLightness = std::max(0.0, *lightness);
+    auto normalizedChroma = std::max(0.0, *chroma);
+    auto normalizedHue = normalizeHue(*hue);
 
-    auto resolvedLightness = resolveRelativeColorChannel<CSSValueL, CSSValueC, CSSValueH, CSSValueAlpha>(*lightness, originColorAsLCH, [](auto lightness) {
-        return std::max(0.0, lightness);
-    });
-    auto resolvedChroma = resolveRelativeColorChannel<CSSValueL, CSSValueC, CSSValueH, CSSValueAlpha>(*chroma, originColorAsLCH, [](auto chroma) {
-        return std::max(0.0, chroma);
-    });
-    auto resolvedHue = resolveRelativeColorChannel<CSSValueL, CSSValueC, CSSValueH, CSSValueAlpha>(*hue, originColorAsLCH, [](auto hue) {
-        return normalizeHue(hue);
-    });
-    auto resolvedAlpha = resolveRelativeColorChannel<CSSValueL, CSSValueC, CSSValueH, CSSValueAlpha>(*alpha, originColorAsLCH);
-
-    return LCHA<float> { static_cast<float>(resolvedLightness), static_cast<float>(resolvedChroma), static_cast<float>(resolvedHue), static_cast<float>(resolvedAlpha) };
+    return LCHA<float> { static_cast<float>(normalizedLightness), static_cast<float>(normalizedChroma), static_cast<float>(normalizedHue), static_cast<float>(*alpha) };
 }
 
 static Color parseLCHParameters(CSSParserTokenRange& range, const CSSParserContext& context)
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to