<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[278261] trunk</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/278261">278261</a></dd>
<dt>Author</dt> <dd>weinig@apple.com</dd>
<dt>Date</dt> <dd>2021-05-30 17:29:18 -0700 (Sun, 30 May 2021)</dd>
</dl>

<h3>Log Message</h3>
<pre>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:</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsChangeLog">trunk/LayoutTests/ChangeLog</a></li>
<li><a href="#trunkLayoutTestsfastcssparsingrelativecolorsyntaxexpectedtxt">trunk/LayoutTests/fast/css/parsing-relative-color-syntax-expected.txt</a></li>
<li><a href="#trunkLayoutTestsfastcssparsingrelativecolorsyntaxhtml">trunk/LayoutTests/fast/css/parsing-relative-color-syntax.html</a></li>
<li><a href="#trunkSourceWebCoreChangeLog">trunk/Source/WebCore/ChangeLog</a></li>
<li><a href="#trunkSourceWebCoreSourcestxt">trunk/Source/WebCore/Sources.txt</a></li>
<li><a href="#trunkSourceWebCoreWebCorexcodeprojprojectpbxproj">trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj</a></li>
<li><a href="#trunkSourceWebCorecsscalcCSSCalcExpressionNodeParsercpp">trunk/Source/WebCore/css/calc/CSSCalcExpressionNodeParser.cpp</a></li>
<li><a href="#trunkSourceWebCorecsscalcCSSCalcExpressionNodeParserh">trunk/Source/WebCore/css/calc/CSSCalcExpressionNodeParser.h</a></li>
<li><a href="#trunkSourceWebCorecsscalcCSSCalcValuecpp">trunk/Source/WebCore/css/calc/CSSCalcValue.cpp</a></li>
<li><a href="#trunkSourceWebCorecsscalcCSSCalcValueh">trunk/Source/WebCore/css/calc/CSSCalcValue.h</a></li>
<li><a href="#trunkSourceWebCorecssmakevaluespl">trunk/Source/WebCore/css/makevalues.pl</a></li>
<li><a href="#trunkSourceWebCorecssparserCSSPropertyParserHelperscpp">trunk/Source/WebCore/css/parser/CSSPropertyParserHelpers.cpp</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunkSourceWebCorecsscalcCSSCalcSymbolTablecpp">trunk/Source/WebCore/css/calc/CSSCalcSymbolTable.cpp</a></li>
<li><a href="#trunkSourceWebCorecsscalcCSSCalcSymbolTableh">trunk/Source/WebCore/css/calc/CSSCalcSymbolTable.h</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkLayoutTestsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/ChangeLog (278260 => 278261)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/ChangeLog      2021-05-30 22:46:54 UTC (rev 278260)
+++ trunk/LayoutTests/ChangeLog 2021-05-31 00:29:18 UTC (rev 278261)
</span><span class="lines">@@ -1,3 +1,16 @@
</span><ins>+2021-05-30  Sam Weinig  <weinig@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:
+
</ins><span class="cx"> 2021-05-29  Cameron McCormack  <heycam@apple.com>
</span><span class="cx"> 
</span><span class="cx">         Rename Internals.mainThreadScrollingReasons to synchronousScrollingReasons
</span></span></pre></div>
<a id="trunkLayoutTestsfastcssparsingrelativecolorsyntaxexpectedtxt"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/fast/css/parsing-relative-color-syntax-expected.txt (278260 => 278261)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/fast/css/parsing-relative-color-syntax-expected.txt    2021-05-30 22:46:54 UTC (rev 278260)
+++ trunk/LayoutTests/fast/css/parsing-relative-color-syntax-expected.txt       2021-05-31 00:29:18 UTC (rev 278261)
</span><span class="lines">@@ -42,20 +42,27 @@
</span><span class="cx"> PASS computedStyle("background-color", "rgb(from rgb(10%, 20%, 30%, 40%) 25 g b / 25%)") is "rgba(25, 51, 77, 0.25)"
</span><span class="cx"> PASS computedStyle("background-color", "rgb(from rgb(10%, 20%, 30%, 40%) r 25 b / 25%)") is "rgba(26, 25, 77, 0.25)"
</span><span class="cx"> PASS computedStyle("background-color", "rgb(from rgb(10%, 20%, 30%, 40%) r g 25 / 25%)") is "rgba(26, 51, 25, 0.25)"
</span><del>-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)"
</del><ins>+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)"
</ins><span class="cx"> PASS computedStyle("background-color", "rgb(from rebeccapurple red g b)") is "rgba(0, 0, 0, 0)"
</span><span class="cx"> PASS computedStyle("background-color", "rgb(from rebeccapurple l g b)") is "rgba(0, 0, 0, 0)"
</span><span class="cx"> PASS computedStyle("background-color", "rgb(from rebeccapurple h g b)") is "rgba(0, 0, 0, 0)"
</span><span class="lines">@@ -91,14 +98,24 @@
</span><span class="cx"> PASS computedStyle("background-color", "hsl(from rgb(10%, 20%, 30%, 40%) h 25% l / alpha)") is "rgba(39, 51, 64, 0.4)"
</span><span class="cx"> PASS computedStyle("background-color", "hsl(from rgb(10%, 20%, 30%, 40%) h s 25% / alpha)") is "rgba(32, 63, 95, 0.4)"
</span><span class="cx"> PASS computedStyle("background-color", "hsl(from rgb(10%, 20%, 30%, 40%) h s l / .25)") is "rgba(26, 51, 77, 0.25)"
</span><del>-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)"
</del><ins>+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)"
</ins><span class="cx"> PASS computedStyle("background-color", "hsl(from rebeccapurple h 10% 10)") is "rgba(0, 0, 0, 0)"
</span><span class="cx"> PASS computedStyle("background-color", "hsl(from rebeccapurple h 10 10%)") is "rgba(0, 0, 0, 0)"
</span><span class="cx"> PASS computedStyle("background-color", "hsl(from rebeccapurple 10% s l)") is "rgba(0, 0, 0, 0)"
</span><span class="lines">@@ -140,14 +157,24 @@
</span><span class="cx"> PASS computedStyle("background-color", "hwb(from rgb(10%, 20%, 30%, 40%) h 25% b / alpha)") is "rgba(64, 70, 77, 0.4)"
</span><span class="cx"> PASS computedStyle("background-color", "hwb(from rgb(10%, 20%, 30%, 40%) h w 25% / alpha)") is "rgba(26, 107, 191, 0.4)"
</span><span class="cx"> PASS computedStyle("background-color", "hwb(from rgb(10%, 20%, 30%, 40%) h w b / .25)") is "rgba(26, 51, 77, 0.25)"
</span><del>-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)"
</del><ins>+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)"
</ins><span class="cx"> PASS computedStyle("background-color", "hwb(from rebeccapurple h 10% 10)") is "rgba(0, 0, 0, 0)"
</span><span class="cx"> PASS computedStyle("background-color", "hwb(from rebeccapurple h 10 10%)") is "rgba(0, 0, 0, 0)"
</span><span class="cx"> PASS computedStyle("background-color", "hwb(from rebeccapurple 10% w b)") is "rgba(0, 0, 0, 0)"
</span><span class="lines">@@ -182,14 +209,16 @@
</span><span class="cx"> PASS computedStyle("background-color", "lab(from lab(25% 20 50 / 40%) l 35 b / alpha)") is "lab(25% 35 50 / 0.4)"
</span><span class="cx"> PASS computedStyle("background-color", "lab(from lab(25% 20 50 / 40%) l a 35 / alpha)") is "lab(25% 20 35 / 0.4)"
</span><span class="cx"> PASS computedStyle("background-color", "lab(from lab(25% 20 50 / 40%) l a b / .35)") is "lab(25% 20 50 / 0.35)"
</span><del>-PASS computedStyle("background-color", "lab(from lab(25% 20 50) l b a)") is "rgba(0, 0, 0, 0)"
</del><ins>+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)"
</ins><span class="cx"> PASS computedStyle("background-color", "lab(from lab(25% 20 50) l alpha a / b)") is "rgba(0, 0, 0, 0)"
</span><del>-PASS computedStyle("background-color", "lab(from lab(25% 20 50) l a a / a)") is "rgba(0, 0, 0, 0)"
</del><span class="cx"> PASS computedStyle("background-color", "lab(from lab(25% 20 50) l alpha alpha / alpha)") is "rgba(0, 0, 0, 0)"
</span><del>-PASS computedStyle("background-color", "lab(from lab(25% 20 50 / 40%) l b a)") is "rgba(0, 0, 0, 0)"
</del><span class="cx"> PASS computedStyle("background-color", "lab(from lab(25% 20 50 / 40%) l alpha a / b)") is "rgba(0, 0, 0, 0)"
</span><del>-PASS computedStyle("background-color", "lab(from lab(25% 20 50 / 40%) l a a / a)") is "rgba(0, 0, 0, 0)"
</del><span class="cx"> PASS computedStyle("background-color", "lab(from lab(25% 20 50 / 40%) l alpha alpha / alpha)") is "rgba(0, 0, 0, 0)"
</span><ins>+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)"
</ins><span class="cx"> PASS computedStyle("background-color", "lab(from lab(25% 20 50) l 10% 10)") is "rgba(0, 0, 0, 0)"
</span><span class="cx"> PASS computedStyle("background-color", "lab(from lab(25% 20 50) l 10 10%)") is "rgba(0, 0, 0, 0)"
</span><span class="cx"> PASS computedStyle("background-color", "lab(from lab(25% 20 50) 10 a b)") is "rgba(0, 0, 0, 0)"
</span><span class="lines">@@ -231,10 +260,22 @@
</span><span class="cx"> PASS computedStyle("background-color", "lch(from lch(70% 45 30 / 40%) l c 25 / alpha)") is "lch(70% 45 25 / 0.4)"
</span><span class="cx"> PASS computedStyle("background-color", "lch(from lch(70% 45 30 / 40%) l c 25deg / alpha)") is "lch(70% 45 25 / 0.4)"
</span><span class="cx"> PASS computedStyle("background-color", "lch(from lch(70% 45 30 / 40%) l c h / .25)") is "lch(70% 45 30 / 0.25)"
</span><del>-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)"
</del><ins>+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)"
</ins><span class="cx"> PASS computedStyle("background-color", "lch(from lch(70% 45 30) l 10% h)") is "rgba(0, 0, 0, 0)"
</span><span class="cx"> PASS computedStyle("background-color", "lch(from lch(70% 45 30) l c 10%)") is "rgba(0, 0, 0, 0)"
</span><span class="cx"> PASS computedStyle("background-color", "lch(from lch(70% 45 30) 10 c h)") is "rgba(0, 0, 0, 0)"
</span><span class="lines">@@ -244,6 +285,21 @@
</span><span class="cx"> PASS computedStyle("background-color", "lch(from lch(70% 45 30) lightness c h)") is "rgba(0, 0, 0, 0)"
</span><span class="cx"> PASS computedStyle("background-color", "lch(from lch(70% 45 30) x c h)") is "rgba(0, 0, 0, 0)"
</span><span class="cx"> PASS computedStyle("background-color", "lch(from lch(70% 45 30) l g b)") is "rgba(0, 0, 0, 0)"
</span><ins>+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)"
</ins><span class="cx"> PASS successfullyParsed is true
</span><span class="cx"> 
</span><span class="cx"> TEST COMPLETE
</span></span></pre></div>
<a id="trunkLayoutTestsfastcssparsingrelativecolorsyntaxhtml"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/fast/css/parsing-relative-color-syntax.html (278260 => 278261)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/fast/css/parsing-relative-color-syntax.html    2021-05-30 22:46:54 UTC (rev 278260)
+++ trunk/LayoutTests/fast/css/parsing-relative-color-syntax.html       2021-05-31 00:29:18 UTC (rev 278261)
</span><span class="lines">@@ -1,6 +1,15 @@
</span><span class="cx"> <!DOCTYPE html><!-- webkit-test-runner [ CSSRelativeColorSyntaxEnabled=true ] -->
</span><span class="cx"> <html>
</span><span class="cx">     <script src="../../resources/js-test-pre.js"></script>
</span><ins>+    <style>
+        html { 
+            --bg-color: blue;
+            --color: green;
+            --accent: lightseagreen;
+            --mycolor: orchid;
+            --mygray: lch(from var(--mycolor) l 0 h);
+        }
+    </style>
</ins><span class="cx"> </head>
</span><span class="cx"> <body>
</span><span class="cx"> <script>
</span><span class="lines">@@ -95,24 +104,33 @@
</span><span class="cx">     testComputed(`rgb(from rgb(10%, 20%, 30%, 40%) r 25 b / 25%)`, `rgba(26, 25, 77, 0.25)`);
</span><span class="cx">     testComputed(`rgb(from rgb(10%, 20%, 30%, 40%) r g 25 / 25%)`, `rgba(26, 51, 25, 0.25)`);
</span><span class="cx"> 
</span><del>-    // 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)`);
</del><ins>+    // 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)`);
</ins><span class="cx"> 
</span><del>-    // 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)`);
</del><ins>+    // 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)`);
</ins><span class="cx"> 
</span><ins>+    // 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)`);
+
</ins><span class="cx">     // Testing invalid component names
</span><span class="cx">     testComputed(`rgb(from rebeccapurple red g b)`, `rgba(0, 0, 0, 0)`);
</span><span class="cx">     testComputed(`rgb(from rebeccapurple l g b)`, `rgba(0, 0, 0, 0)`);
</span><span class="lines">@@ -161,16 +179,30 @@
</span><span class="cx">     testComputed(`hsl(from rgb(10%, 20%, 30%, 40%) h s 25% / alpha)`, `rgba(32, 63, 95, 0.4)`);
</span><span class="cx">     testComputed(`hsl(from rgb(10%, 20%, 30%, 40%) h s l / .25)`, `rgba(26, 51, 77, 0.25)`);
</span><span class="cx"> 
</span><del>-    // 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)`);
</del><ins>+    // 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)`);
</ins><span class="cx"> 
</span><ins>+    // 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)`);
+
</ins><span class="cx">     // Testing invalid values.
</span><span class="cx">     testComputed(`hsl(from rebeccapurple h 10% 10)`, `rgba(0, 0, 0, 0)`);
</span><span class="cx">     testComputed(`hsl(from rebeccapurple h 10 10%)`, `rgba(0, 0, 0, 0)`);
</span><span class="lines">@@ -228,16 +260,30 @@
</span><span class="cx">     testComputed(`hwb(from rgb(10%, 20%, 30%, 40%) h w 25% / alpha)`, `rgba(26, 107, 191, 0.4)`);
</span><span class="cx">     testComputed(`hwb(from rgb(10%, 20%, 30%, 40%) h w b / .25)`, `rgba(26, 51, 77, 0.25)`);
</span><span class="cx"> 
</span><del>-    // 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)`);
</del><ins>+    // 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)`);
</ins><span class="cx"> 
</span><ins>+    // 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)`);
+
</ins><span class="cx">     // Testing invalid values.
</span><span class="cx">     testComputed(`hwb(from rebeccapurple h 10% 10)`, `rgba(0, 0, 0, 0)`);
</span><span class="cx">     testComputed(`hwb(from rebeccapurple h 10 10%)`, `rgba(0, 0, 0, 0)`);
</span><span class="lines">@@ -288,16 +334,22 @@
</span><span class="cx">     testComputed(`lab(from lab(25% 20 50 / 40%) l a 35 / alpha)`, `lab(25% 20 35 / 0.4)`);
</span><span class="cx">     testComputed(`lab(from lab(25% 20 50 / 40%) l a b / .35)`, `lab(25% 20 50 / 0.35)`);
</span><span class="cx"> 
</span><del>-    // 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)`);
</del><ins>+    // 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).
</ins><span class="cx">     testComputed(`lab(from lab(25% 20 50) l alpha a / b)`, `rgba(0, 0, 0, 0)`);
</span><del>-    testComputed(`lab(from lab(25% 20 50) l a a / a)`, `rgba(0, 0, 0, 0)`);
</del><span class="cx">     testComputed(`lab(from lab(25% 20 50) l alpha alpha / alpha)`, `rgba(0, 0, 0, 0)`);
</span><del>-    testComputed(`lab(from lab(25% 20 50 / 40%) l b a)`, `rgba(0, 0, 0, 0)`);
</del><span class="cx">     testComputed(`lab(from lab(25% 20 50 / 40%) l alpha a / b)`, `rgba(0, 0, 0, 0)`);
</span><del>-    testComputed(`lab(from lab(25% 20 50 / 40%) l a a / a)`, `rgba(0, 0, 0, 0)`);
</del><span class="cx">     testComputed(`lab(from lab(25% 20 50 / 40%) l alpha alpha / alpha)`, `rgba(0, 0, 0, 0)`);
</span><span class="cx"> 
</span><ins>+    // 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)`);
+
</ins><span class="cx">     // Testing invalid values.
</span><span class="cx">     testComputed(`lab(from lab(25% 20 50) l 10% 10)`, `rgba(0, 0, 0, 0)`);
</span><span class="cx">     testComputed(`lab(from lab(25% 20 50) l 10 10%)`, `rgba(0, 0, 0, 0)`);
</span><span class="lines">@@ -355,12 +407,29 @@
</span><span class="cx">     testComputed(`lch(from lch(70% 45 30 / 40%) l c 25deg / alpha)`, `lch(70% 45 25 / 0.4)`);
</span><span class="cx">     testComputed(`lch(from lch(70% 45 30 / 40%) l c h / .25)`, `lch(70% 45 30 / 0.25)`);
</span><span class="cx"> 
</span><del>-    // 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)`);
</del><ins>+    // 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)`);
</ins><span class="cx"> 
</span><ins>+    // 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)`);
+
</ins><span class="cx">     // Testing invalid values.
</span><span class="cx">     testComputed(`lch(from lch(70% 45 30) l 10% h)`, `rgba(0, 0, 0, 0)`);
</span><span class="cx">     testComputed(`lch(from lch(70% 45 30) l c 10%)`, `rgba(0, 0, 0, 0)`);
</span><span class="lines">@@ -373,6 +442,44 @@
</span><span class="cx">     testComputed(`lch(from lch(70% 45 30) lightness c h)`, `rgba(0, 0, 0, 0)`);
</span><span class="cx">     testComputed(`lch(from lch(70% 45 30) x c h)`, `rgba(0, 0, 0, 0)`);
</span><span class="cx">     testComputed(`lch(from lch(70% 45 30) l g b)`, `rgba(0, 0, 0, 0)`);
</span><ins>+
+
+    // 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)`);
+    
</ins><span class="cx"> </script>
</span><span class="cx">     
</span><span class="cx"> <script src="../../resources/js-test-post.js"></script>
</span></span></pre></div>
<a id="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (278260 => 278261)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/ChangeLog   2021-05-30 22:46:54 UTC (rev 278260)
+++ trunk/Source/WebCore/ChangeLog      2021-05-31 00:29:18 UTC (rev 278261)
</span><span class="lines">@@ -1,3 +1,85 @@
</span><ins>+2021-05-30  Sam Weinig  <weinig@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.
+
</ins><span class="cx"> 2021-05-30  Dean Jackson  <dino@apple.com>
</span><span class="cx"> 
</span><span class="cx">         [WebXR] Provide a way to bind and unbind IOSurfaces to ANGLE Pbuffers
</span></span></pre></div>
<a id="trunkSourceWebCoreSourcestxt"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/Sources.txt (278260 => 278261)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/Sources.txt 2021-05-30 22:46:54 UTC (rev 278260)
+++ trunk/Source/WebCore/Sources.txt    2021-05-31 00:29:18 UTC (rev 278261)
</span><span class="lines">@@ -831,6 +831,7 @@
</span><span class="cx"> css/calc/CSSCalcNegateNode.cpp
</span><span class="cx"> css/calc/CSSCalcOperationNode.cpp
</span><span class="cx"> css/calc/CSSCalcPrimitiveValueNode.cpp
</span><ins>+css/calc/CSSCalcSymbolTable.cpp
</ins><span class="cx"> css/calc/CSSCalcValue.cpp
</span><span class="cx"> css/parser/CSSAtRuleID.cpp
</span><span class="cx"> css/parser/CSSDeferredParser.cpp
</span></span></pre></div>
<a id="trunkSourceWebCoreWebCorexcodeprojprojectpbxproj"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj (278260 => 278261)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj   2021-05-30 22:46:54 UTC (rev 278260)
+++ trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj      2021-05-31 00:29:18 UTC (rev 278261)
</span><span class="lines">@@ -4077,6 +4077,7 @@
</span><span class="cx">          BC6932740D7E293900AE44D1 /* JSDOMWindowBase.h in Headers */ = {isa = PBXBuildFile; fileRef = BC6932720D7E293900AE44D1 /* JSDOMWindowBase.h */; settings = {ATTRIBUTES = (Private, ); }; };
</span><span class="cx">          BC6D44ED0C07F2ED0072D2C9 /* JSHTMLEmbedElement.h in Headers */ = {isa = PBXBuildFile; fileRef = BC6D44EB0C07F2ED0072D2C9 /* JSHTMLEmbedElement.h */; };
</span><span class="cx">          BC6D6E2609AF943500F59759 /* ScrollView.h in Headers */ = {isa = PBXBuildFile; fileRef = BC6D6E2509AF943500F59759 /* ScrollView.h */; settings = {ATTRIBUTES = (Private, ); }; };
</span><ins>+               BC709DE0266323CF00B9A21C /* CSSCalcSymbolTable.h in Headers */ = {isa = PBXBuildFile; fileRef = BC709DDE266323CE00B9A21C /* CSSCalcSymbolTable.h */; };
</ins><span class="cx">           BC772B3C0C4EA91E0083285F /* CSSHelper.h in Headers */ = {isa = PBXBuildFile; fileRef = BC772B360C4EA91E0083285F /* CSSHelper.h */; settings = {ATTRIBUTES = (Private, ); }; };
</span><span class="cx">          BC772C470C4EB2C60083285F /* XMLHttpRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = BC772C450C4EB2C60083285F /* XMLHttpRequest.h */; };
</span><span class="cx">          BC772C4F0C4EB3040083285F /* MIMETypeRegistry.h in Headers */ = {isa = PBXBuildFile; fileRef = BC772C4D0C4EB3040083285F /* MIMETypeRegistry.h */; settings = {ATTRIBUTES = (Private, ); }; };
</span><span class="lines">@@ -14304,6 +14305,8 @@
</span><span class="cx">          BC6D44EB0C07F2ED0072D2C9 /* JSHTMLEmbedElement.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = JSHTMLEmbedElement.h; sourceTree = "<group>"; };
</span><span class="cx">          BC6D6E2509AF943500F59759 /* ScrollView.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = ScrollView.h; sourceTree = "<group>"; };
</span><span class="cx">          BC6EB84526266B61003225A7 /* ColorLuminance.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ColorLuminance.cpp; sourceTree = "<group>"; };
</span><ins>+               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>"; };
</ins><span class="cx">           BC772B360C4EA91E0083285F /* CSSHelper.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = CSSHelper.h; sourceTree = "<group>"; };
</span><span class="cx">          BC772C440C4EB2C60083285F /* XMLHttpRequest.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = XMLHttpRequest.cpp; sourceTree = "<group>"; };
</span><span class="cx">          BC772C450C4EB2C60083285F /* XMLHttpRequest.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = XMLHttpRequest.h; sourceTree = "<group>"; };
</span><span class="lines">@@ -27956,6 +27959,8 @@
</span><span class="cx">                          BCF1645F2662A28D0002F7EF /* CSSCalcOperationNode.h */,
</span><span class="cx">                          BCF164542662A1220002F7EF /* CSSCalcPrimitiveValueNode.cpp */,
</span><span class="cx">                          BCF164532662A1220002F7EF /* CSSCalcPrimitiveValueNode.h */,
</span><ins>+                               BC709DDF266323CE00B9A21C /* CSSCalcSymbolTable.cpp */,
+                               BC709DDE266323CE00B9A21C /* CSSCalcSymbolTable.h */,
</ins><span class="cx">                           49AE2D8C134EE50C0072920A /* CSSCalcValue.cpp */,
</span><span class="cx">                          49AE2D8D134EE50C0072920A /* CSSCalcValue.h */,
</span><span class="cx">                  );
</span><span class="lines">@@ -34669,6 +34674,7 @@
</span><span class="cx">                          26601EBF14B3B9AD0012C0FE /* PlatformEventFactoryIOS.h in Headers */,
</span><span class="cx">                          BCAA487014A052530088FAC4 /* PlatformEventFactoryMac.h in Headers */,
</span><span class="cx">                          A723F77B1484CA4C008C6DBE /* PlatformExportMacros.h in Headers */,
</span><ins>+                               BC709DE0266323CF00B9A21C /* CSSCalcSymbolTable.h in Headers */,
</ins><span class="cx">                           515BE1951D54F5FB00DD7C68 /* PlatformGamepad.h in Headers */,
</span><span class="cx">                          726D56E2253AE28D0002EF90 /* PlatformImage.h in Headers */,
</span><span class="cx">                          2D7705C7255276CD001D0C94 /* PlatformImageBuffer.h in Headers */,
</span></span></pre></div>
<a id="trunkSourceWebCorecsscalcCSSCalcExpressionNodeParsercpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/css/calc/CSSCalcExpressionNodeParser.cpp (278260 => 278261)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/css/calc/CSSCalcExpressionNodeParser.cpp    2021-05-30 22:46:54 UTC (rev 278260)
+++ trunk/Source/WebCore/css/calc/CSSCalcExpressionNodeParser.cpp       2021-05-31 00:29:18 UTC (rev 278261)
</span><span class="lines">@@ -31,9 +31,11 @@
</span><span class="cx"> #include "CSSCalcNegateNode.h"
</span><span class="cx"> #include "CSSCalcOperationNode.h"
</span><span class="cx"> #include "CSSCalcPrimitiveValueNode.h"
</span><ins>+#include "CSSCalcSymbolTable.h"
</ins><span class="cx"> #include "CSSCalcValue.h"
</span><span class="cx"> #include "CSSParserToken.h"
</span><span class="cx"> #include "CSSParserTokenRange.h"
</span><ins>+#include "CSSValueKeywords.h"
</ins><span class="cx"> #include "Logging.h"
</span><span class="cx"> 
</span><span class="cx"> namespace WebCore {
</span><span class="lines">@@ -180,19 +182,35 @@
</span><span class="cx"> 
</span><span class="cx"> bool CSSCalcExpressionNodeParser::parseValue(CSSParserTokenRange& tokens, RefPtr<CSSCalcExpressionNode>& result)
</span><span class="cx"> {
</span><del>-    // FIXME: Add code here to parse CSSValidID for named constants.
</del><ins>+    auto makeCSSCalcPrimitiveValueNode = [&] (CSSUnitType type, double value) -> bool {
+        if (calcUnitCategory(type) == CalculationCategory::Other)
+            return false;
+        
+        result = CSSCalcPrimitiveValueNode::create(CSSPrimitiveValue::create(value, type));
+        return true;
+    };
</ins><span class="cx"> 
</span><del>-    CSSParserToken token = tokens.consumeIncludingWhitespace();
-    if (!(token.type() == NumberToken || token.type() == PercentageToken || token.type() == DimensionToken))
</del><ins>+    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:
</ins><span class="cx">         return false;
</span><del>-    
-    auto type = token.unitType();
-    if (calcUnitCategory(type) == CalculationCategory::Other)
-        return false;
-    
-    result = CSSCalcPrimitiveValueNode::create(CSSPrimitiveValue::create(token.numericValue(), type));
-    
-    return true;
</del><ins>+    }
+
+    ASSERT_NOT_REACHED();
+    return false;
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> bool CSSCalcExpressionNodeParser::parseCalcValue(CSSParserTokenRange& tokens, int depth, RefPtr<CSSCalcExpressionNode>& result)
</span></span></pre></div>
<a id="trunkSourceWebCorecsscalcCSSCalcExpressionNodeParserh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/css/calc/CSSCalcExpressionNodeParser.h (278260 => 278261)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/css/calc/CSSCalcExpressionNodeParser.h      2021-05-30 22:46:54 UTC (rev 278260)
+++ trunk/Source/WebCore/css/calc/CSSCalcExpressionNodeParser.h 2021-05-31 00:29:18 UTC (rev 278261)
</span><span class="lines">@@ -25,7 +25,6 @@
</span><span class="cx"> 
</span><span class="cx"> #pragma once
</span><span class="cx"> 
</span><del>-#include "CSSValueKeywords.h"
</del><span class="cx"> #include "CalcOperator.h"
</span><span class="cx"> #include "CalculationCategory.h"
</span><span class="cx"> 
</span><span class="lines">@@ -32,13 +31,17 @@
</span><span class="cx"> namespace WebCore {
</span><span class="cx"> 
</span><span class="cx"> class CSSCalcExpressionNode;
</span><ins>+class CSSCalcSymbolTable;
</ins><span class="cx"> class CSSParserToken;
</span><span class="cx"> class CSSParserTokenRange;
</span><span class="cx"> 
</span><ins>+enum CSSValueID : uint16_t;
+
</ins><span class="cx"> class CSSCalcExpressionNodeParser {
</span><span class="cx"> public:
</span><del>-    explicit CSSCalcExpressionNodeParser(CalculationCategory destinationCategory)
</del><ins>+    explicit CSSCalcExpressionNodeParser(CalculationCategory destinationCategory, const CSSCalcSymbolTable& symbolTable)
</ins><span class="cx">         : m_destinationCategory(destinationCategory)
</span><ins>+        , m_symbolTable(symbolTable)
</ins><span class="cx">     {
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="lines">@@ -55,6 +58,7 @@
</span><span class="cx">     bool parseCalcValue(CSSParserTokenRange&, int depth, RefPtr<CSSCalcExpressionNode>&);
</span><span class="cx"> 
</span><span class="cx">     CalculationCategory m_destinationCategory;
</span><ins>+    const CSSCalcSymbolTable& m_symbolTable;
</ins><span class="cx"> };
</span><span class="cx"> 
</span><span class="cx"> }
</span></span></pre></div>
<a id="trunkSourceWebCorecsscalcCSSCalcSymbolTablecppfromrev278260trunkSourceWebCorecsscalcCSSCalcExpressionNodeParserh"></a>
<div class="copfile"><h4>Copied: trunk/Source/WebCore/css/calc/CSSCalcSymbolTable.cpp (from rev 278260, trunk/Source/WebCore/css/calc/CSSCalcExpressionNodeParser.h) (0 => 278261)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/css/calc/CSSCalcSymbolTable.cpp                             (rev 0)
+++ trunk/Source/WebCore/css/calc/CSSCalcSymbolTable.cpp        2021-05-31 00:29:18 UTC (rev 278261)
</span><span class="lines">@@ -0,0 +1,48 @@
</span><ins>+/*
+ * 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 }};
+}
+
+}
</ins></span></pre></div>
<a id="trunkSourceWebCorecsscalcCSSCalcSymbolTablehfromrev278260trunkSourceWebCorecsscalcCSSCalcExpressionNodeParserh"></a>
<div class="copfile"><h4>Copied: trunk/Source/WebCore/css/calc/CSSCalcSymbolTable.h (from rev 278260, trunk/Source/WebCore/css/calc/CSSCalcExpressionNodeParser.h) (0 => 278261)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/css/calc/CSSCalcSymbolTable.h                               (rev 0)
+++ trunk/Source/WebCore/css/calc/CSSCalcSymbolTable.h  2021-05-31 00:29:18 UTC (rev 278261)
</span><span class="lines">@@ -0,0 +1,52 @@
</span><ins>+/*
+ * 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;
+};
+
+};
</ins></span></pre></div>
<a id="trunkSourceWebCorecsscalcCSSCalcValuecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/css/calc/CSSCalcValue.cpp (278260 => 278261)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/css/calc/CSSCalcValue.cpp   2021-05-30 22:46:54 UTC (rev 278260)
+++ trunk/Source/WebCore/css/calc/CSSCalcValue.cpp      2021-05-31 00:29:18 UTC (rev 278261)
</span><span class="lines">@@ -40,6 +40,7 @@
</span><span class="cx"> #include "CSSParser.h"
</span><span class="cx"> #include "CSSParserTokenRange.h"
</span><span class="cx"> #include "CSSPrimitiveValueMappings.h"
</span><ins>+#include "CSSValueKeywords.h"
</ins><span class="cx"> #include "CalcExpressionBlendLength.h"
</span><span class="cx"> #include "CalcExpressionLength.h"
</span><span class="cx"> #include "Logging.h"
</span><span class="lines">@@ -299,9 +300,9 @@
</span><span class="cx">     ts << ")\n";
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-RefPtr<CSSCalcValue> CSSCalcValue::create(CSSValueID function, const CSSParserTokenRange& tokens, CalculationCategory destinationCategory, ValueRange range)
</del><ins>+RefPtr<CSSCalcValue> CSSCalcValue::create(CSSValueID function, const CSSParserTokenRange& tokens, CalculationCategory destinationCategory, ValueRange range, const CSSCalcSymbolTable& symbolTable)
</ins><span class="cx"> {
</span><del>-    CSSCalcExpressionNodeParser parser(destinationCategory);
</del><ins>+    CSSCalcExpressionNodeParser parser(destinationCategory, symbolTable);
</ins><span class="cx">     auto expression = parser.parseCalc(tokens, function);
</span><span class="cx">     if (!expression)
</span><span class="cx">         return nullptr;
</span><span class="lines">@@ -309,7 +310,12 @@
</span><span class="cx">     LOG_WITH_STREAM(Calc, stream << "CSSCalcValue::create " << *result);
</span><span class="cx">     return result;
</span><span class="cx"> }
</span><del>-    
</del><ins>+
+RefPtr<CSSCalcValue> CSSCalcValue::create(CSSValueID function, const CSSParserTokenRange& tokens, CalculationCategory destinationCategory, ValueRange range)
+{
+    return create(function, tokens, destinationCategory, range, { });
+}
+
</ins><span class="cx"> RefPtr<CSSCalcValue> CSSCalcValue::create(const CalculationValue& value, const RenderStyle& style)
</span><span class="cx"> {
</span><span class="cx">     auto expression = createCSS(value.expression(), style);
</span></span></pre></div>
<a id="trunkSourceWebCorecsscalcCSSCalcValueh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/css/calc/CSSCalcValue.h (278260 => 278261)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/css/calc/CSSCalcValue.h     2021-05-30 22:46:54 UTC (rev 278260)
+++ trunk/Source/WebCore/css/calc/CSSCalcValue.h        2021-05-31 00:29:18 UTC (rev 278261)
</span><span class="lines">@@ -31,7 +31,6 @@
</span><span class="cx"> #pragma once
</span><span class="cx"> 
</span><span class="cx"> #include "CSSValue.h"
</span><del>-#include "CSSValueKeywords.h"
</del><span class="cx"> #include "CalculationValue.h"
</span><span class="cx"> #include <wtf/Forward.h>
</span><span class="cx"> #include <wtf/Ref.h>
</span><span class="lines">@@ -39,6 +38,7 @@
</span><span class="cx"> namespace WebCore {
</span><span class="cx"> 
</span><span class="cx"> class CSSCalcExpressionNode;
</span><ins>+class CSSCalcSymbolTable;
</ins><span class="cx"> class CSSParserTokenRange;
</span><span class="cx"> class CSSToLengthConversionData;
</span><span class="cx"> class RenderStyle;
</span><span class="lines">@@ -47,8 +47,11 @@
</span><span class="cx"> enum class CalculationCategory : uint8_t;
</span><span class="cx"> enum class ValueRange : uint8_t;
</span><span class="cx"> 
</span><ins>+enum CSSValueID : uint16_t;
+
</ins><span class="cx"> class CSSCalcValue final : public CSSValue {
</span><span class="cx"> public:
</span><ins>+    static RefPtr<CSSCalcValue> create(CSSValueID function, const CSSParserTokenRange&, CalculationCategory destinationCategory, ValueRange, const CSSCalcSymbolTable&);
</ins><span class="cx">     static RefPtr<CSSCalcValue> create(CSSValueID function, const CSSParserTokenRange&, CalculationCategory destinationCategory, ValueRange);
</span><span class="cx">     static RefPtr<CSSCalcValue> create(const CalculationValue&, const RenderStyle&);
</span><span class="cx">     ~CSSCalcValue();
</span></span></pre></div>
<a id="trunkSourceWebCorecssmakevaluespl"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/css/makevalues.pl (278260 => 278261)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/css/makevalues.pl   2021-05-30 22:46:54 UTC (rev 278260)
+++ trunk/Source/WebCore/css/makevalues.pl      2021-05-31 00:29:18 UTC (rev 278261)
</span><span class="lines">@@ -164,10 +164,12 @@
</span><span class="cx"> 
</span><span class="cx"> #include <string.h>
</span><span class="cx"> #include <wtf/Forward.h>
</span><ins>+#include <wtf/HashFunctions.h>
+#include <wtf/HashTraits.h>
</ins><span class="cx"> 
</span><span class="cx"> namespace WebCore {
</span><span class="cx"> 
</span><del>-enum CSSValueID {
</del><ins>+enum CSSValueID : uint16_t {
</ins><span class="cx">     CSSValueInvalid = 0,
</span><span class="cx"> EOF
</span><span class="cx"> 
</span><span class="lines">@@ -204,6 +206,12 @@
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> } // namespace WebCore
</span><ins>+
+namespace WTF {
+template<> struct DefaultHash<WebCore::CSSValueID> : IntHash<unsigned> { };
+template<> struct HashTraits<WebCore::CSSValueID> : StrongEnumHashTraits<WebCore::CSSValueID> { };
+} // namespace WTF
+
</ins><span class="cx"> EOF
</span><span class="cx"> close HEADER;
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebCorecssparserCSSPropertyParserHelperscpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/css/parser/CSSPropertyParserHelpers.cpp (278260 => 278261)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/css/parser/CSSPropertyParserHelpers.cpp     2021-05-30 22:46:54 UTC (rev 278260)
+++ trunk/Source/WebCore/css/parser/CSSPropertyParserHelpers.cpp        2021-05-31 00:29:18 UTC (rev 278261)
</span><span class="lines">@@ -30,6 +30,7 @@
</span><span class="cx"> #include "config.h"
</span><span class="cx"> #include "CSSPropertyParserHelpers.h"
</span><span class="cx"> 
</span><ins>+#include "CSSCalcSymbolTable.h"
</ins><span class="cx"> #include "CSSCalcValue.h"
</span><span class="cx"> #include "CSSCanvasValue.h"
</span><span class="cx"> #include "CSSCrossfadeValue.h"
</span><span class="lines">@@ -98,7 +99,7 @@
</span><span class="cx"> // FIXME: consider pulling in the parsing logic from CSSCalcExpressionNodeParser.
</span><span class="cx"> class CalcParser {
</span><span class="cx"> public:
</span><del>-    explicit CalcParser(CSSParserTokenRange& range, CalculationCategory destinationCategory, ValueRange valueRange = ValueRange::All, CSSValuePool& cssValuePool = CSSValuePool::singleton())
</del><ins>+    explicit CalcParser(CSSParserTokenRange& range, CalculationCategory destinationCategory, ValueRange valueRange = ValueRange::All, const CSSCalcSymbolTable& symbolTable = { }, CSSValuePool& cssValuePool = CSSValuePool::singleton())
</ins><span class="cx">         : m_sourceRange(range)
</span><span class="cx">         , m_range(range)
</span><span class="cx">         , m_valuePool(cssValuePool)
</span><span class="lines">@@ -106,7 +107,7 @@
</span><span class="cx">         const CSSParserToken& token = range.peek();
</span><span class="cx">         auto functionId = token.functionId();
</span><span class="cx">         if (CSSCalcValue::isCalcFunction(functionId))
</span><del>-            m_calcValue = CSSCalcValue::create(functionId, consumeFunction(m_range), destinationCategory, valueRange);
</del><ins>+            m_calcValue = CSSCalcValue::create(functionId, consumeFunction(m_range), destinationCategory, valueRange, symbolTable);
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     const CSSCalcValue* value() const { return m_calcValue.get(); }
</span><span class="lines">@@ -119,6 +120,14 @@
</span><span class="cx">         return m_valuePool.createValue(WTFMove(m_calcValue));
</span><span class="cx">     }
</span><span class="cx"> 
</span><ins>+    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));
+    }
+
</ins><span class="cx">     RefPtr<CSSPrimitiveValue> consumeInteger(double minimumValue)
</span><span class="cx">     {
</span><span class="cx">         if (!m_calcValue)
</span><span class="lines">@@ -217,11 +226,8 @@
</span><span class="cx">         return std::nullopt;
</span><span class="cx"> 
</span><span class="cx">     CalcParser calcParser(range, CalculationCategory::Number);
</span><del>-    if (const CSSCalcValue* calculation = calcParser.value()) {
-        if (calculation->category() != CalculationCategory::Number)
-            return std::nullopt;
</del><ins>+    if (auto calculation = calcParser.value(); calculation && calculation->category() == CalculationCategory::Number)
</ins><span class="cx">         return calcParser.consumeIntegerTypeRaw<IntType>(minimumValue);
</span><del>-    }
</del><span class="cx"> 
</span><span class="cx">     return std::nullopt;
</span><span class="cx"> }
</span><span class="lines">@@ -272,11 +278,7 @@
</span><span class="cx">     const CSSParserToken& token = range.peek();
</span><span class="cx">     if (token.type() == FunctionToken) {
</span><span class="cx">         CalcParser calcParser(range, CalculationCategory::Number, valueRange);
</span><del>-        if (const auto* calcValue = calcParser.value()) {
-            if (calcValue->category() == CalculationCategory::Number)
-                return calcParser.consumeValue();
-        }
-        return nullptr;
</del><ins>+        return calcParser.consumeValueIfCategory(CalculationCategory::Number);
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     if (auto number = consumeNumberRaw(range, valueRange))
</span><span class="lines">@@ -398,8 +400,7 @@
</span><span class="cx">     const CSSParserToken& token = range.peek();
</span><span class="cx">     if (token.type() == FunctionToken) {
</span><span class="cx">         CalcParser calcParser(range, CalculationCategory::Length, valueRange);
</span><del>-        if (calcParser.value() && calcParser.value()->category() == CalculationCategory::Length)
-            return calcParser.consumeValue();
</del><ins>+        return calcParser.consumeValueIfCategory(CalculationCategory::Length);
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     if (auto result = consumeLengthRaw(range, cssParserMode, valueRange, unitless))
</span><span class="lines">@@ -433,12 +434,8 @@
</span><span class="cx"> {
</span><span class="cx">     const CSSParserToken& token = range.peek();
</span><span class="cx">     if (token.type() == FunctionToken) {
</span><del>-        CalcParser calcParser(range, CalculationCategory::Percent, valueRange, cssValuePool);
-        if (const CSSCalcValue* calculation = calcParser.value()) {
-            if (calculation->category() == CalculationCategory::Percent)
-                return calcParser.consumeValue();
-        }
-        return nullptr;
</del><ins>+        CalcParser calcParser(range, CalculationCategory::Percent, valueRange, { }, cssValuePool);
+        return calcParser.consumeValueIfCategory(CalculationCategory::Percent);
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     if (auto percent = consumePercentRaw(range, valueRange))
</span><span class="lines">@@ -479,10 +476,8 @@
</span><span class="cx">         return std::nullopt;
</span><span class="cx"> 
</span><span class="cx">     CalcParser calcParser(range, CalculationCategory::Length, valueRange);
</span><del>-    if (const CSSCalcValue* calculation = calcParser.value()) {
-        if (canConsumeCalcValue(calculation->category(), cssParserMode))
-            return calcParser.consumeLengthOrPercentRaw();
-    }
</del><ins>+    if (auto calculation = calcParser.value(); calculation && canConsumeCalcValue(calculation->category(), cssParserMode))
+        return calcParser.consumeLengthOrPercentRaw();
</ins><span class="cx">     return std::nullopt;
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="lines">@@ -491,10 +486,8 @@
</span><span class="cx">     const CSSParserToken& token = range.peek();
</span><span class="cx">     if (token.type() == FunctionToken) {
</span><span class="cx">         CalcParser calcParser(range, CalculationCategory::Length, valueRange);
</span><del>-        if (const CSSCalcValue* calculation = calcParser.value()) {
-            if (canConsumeCalcValue(calculation->category(), cssParserMode))
-                return calcParser.consumeValue();
-        }
</del><ins>+        if (auto calculation = calcParser.value(); calculation && canConsumeCalcValue(calculation->category(), cssParserMode))
+            return calcParser.consumeValue();
</ins><span class="cx">         return nullptr;
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="lines">@@ -543,12 +536,8 @@
</span><span class="cx"> {
</span><span class="cx">     const CSSParserToken& token = range.peek();
</span><span class="cx">     if (token.type() == FunctionToken) {
</span><del>-        CalcParser calcParser(range, CalculationCategory::Angle, ValueRange::All, cssValuePool);
-        if (const CSSCalcValue* calculation = calcParser.value()) {
-            if (calculation->category() == CalculationCategory::Angle)
-                return calcParser.consumeValue();
-        }
-        return nullptr;
</del><ins>+        CalcParser calcParser(range, CalculationCategory::Angle, ValueRange::All, { }, cssValuePool);
+        return calcParser.consumeValueIfCategory(CalculationCategory::Angle);
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     if (auto angle = consumeAngleRaw(range, cssParserMode, unitless, unitlessZero))
</span><span class="lines">@@ -560,7 +549,8 @@
</span><span class="cx"> static RefPtr<CSSPrimitiveValue> consumeAngleOrPercent(CSSParserTokenRange& range, CSSParserMode cssParserMode, ValueRange valueRange, UnitlessQuirk unitless, UnitlessZeroQuirk unitlessZero)
</span><span class="cx"> {
</span><span class="cx">     const CSSParserToken& token = range.peek();
</span><del>-    if (token.type() == DimensionToken) {
</del><ins>+    switch (token.type()) {
+    case DimensionToken:
</ins><span class="cx">         switch (token.unitType()) {
</span><span class="cx">         case CSSUnitType::CSS_DEG:
</span><span class="cx">         case CSSUnitType::CSS_RAD:
</span><span class="lines">@@ -568,29 +558,38 @@
</span><span class="cx">         case CSSUnitType::CSS_TURN:
</span><span class="cx">             return CSSValuePool::singleton().createValue(range.consumeIncludingWhitespace().numericValue(), token.unitType());
</span><span class="cx">         default:
</span><del>-            return nullptr;
</del><ins>+            break;
</ins><span class="cx">         }
</span><del>-    }
-    if (token.type() == NumberToken && shouldAcceptUnitlessValue(token.numericValue(), cssParserMode, unitless, unitlessZero))
-        return CSSValuePool::singleton().createValue(range.consumeIncludingWhitespace().numericValue(), CSSUnitType::CSS_DEG);
</del><ins>+        break;
+    
+    case NumberToken:
+        if (shouldAcceptUnitlessValue(token.numericValue(), cssParserMode, unitless, unitlessZero))
+            return CSSValuePool::singleton().createValue(range.consumeIncludingWhitespace().numericValue(), CSSUnitType::CSS_DEG);
+        break;
</ins><span class="cx"> 
</span><del>-    if (token.type() == PercentageToken)
</del><ins>+    case PercentageToken:
</ins><span class="cx">         return consumePercent(range, valueRange);
</span><span class="cx"> 
</span><del>-     if (token.type() != FunctionToken)
-         return nullptr;
</del><ins>+    case FunctionToken: {
+        CalcParser angleCalcParser(range, CalculationCategory::Angle, valueRange);
+        if (const CSSCalcValue* calculation = angleCalcParser.value()) {
+            if (calculation->category() == CalculationCategory::Angle)
+                return angleCalcParser.consumeValue();
+        }
</ins><span class="cx"> 
</span><del>-    CalcParser angleCalcParser(range, CalculationCategory::Angle, valueRange);
-    if (const CSSCalcValue* calculation = angleCalcParser.value()) {
-        if (calculation->category() == CalculationCategory::Angle)
-            return angleCalcParser.consumeValue();
</del><ins>+        CalcParser percentCalcParser(range, CalculationCategory::Percent, valueRange);
+        if (const CSSCalcValue* calculation = percentCalcParser.value()) {
+            if (calculation->category() == CalculationCategory::Percent)
+                return percentCalcParser.consumeValue();
+        }
+    
+        break;
</ins><span class="cx">     }
</span><ins>+    
+    default:
+        break;
+    }
</ins><span class="cx"> 
</span><del>-    CalcParser percentCalcParser(range, CalculationCategory::Percent, valueRange);
-    if (const CSSCalcValue* calculation = percentCalcParser.value()) {
-        if (calculation->category() == CalculationCategory::Percent)
-            return percentCalcParser.consumeValue();
-    }
</del><span class="cx">     return nullptr;
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="lines">@@ -613,11 +612,7 @@
</span><span class="cx">         return nullptr;
</span><span class="cx"> 
</span><span class="cx">     CalcParser calcParser(range, CalculationCategory::Time, valueRange);
</span><del>-    if (const CSSCalcValue* calculation = calcParser.value()) {
-        if (calculation->category() == CalculationCategory::Time)
-            return calcParser.consumeValue();
-    }
-    return nullptr;
</del><ins>+    return calcParser.consumeValueIfCategory(CalculationCategory::Time);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> RefPtr<CSSPrimitiveValue> consumeResolution(CSSParserTokenRange& range, AllowXResolutionUnit allowX)
</span><span class="lines">@@ -743,19 +738,67 @@
</span><span class="cx">         return 1.0;
</span><span class="cx"> 
</span><span class="cx">     if (auto alphaParameter = consumeNumberOrPercentDividedBy100Raw(range))
</span><del>-        return clampTo(*alphaParameter, 0.0, 1.0);
</del><ins>+        return std::clamp(*alphaParameter, 0.0, 1.0);
</ins><span class="cx"> 
</span><span class="cx">     return std::nullopt;
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-template<CSSValueID... allowedIdents> static std::optional<Variant<double, CSSValueID>> consumeOptionalAlphaOrIdent(CSSParserTokenRange& range)
</del><ins>+static std::optional<double> consumeOptionalAlphaOrIdent(CSSParserTokenRange& range, const CSSCalcSymbolTable& symbolTable)
</ins><span class="cx"> {
</span><del>-    if (auto alpha = consumeOptionalAlpha(range))
-        return { *alpha };
</del><ins>+    if (!consumeSlashIncludingWhitespace(range))
+        return 1.0;
</ins><span class="cx"> 
</span><del>-    if (auto ident = consumeIdentRaw<allowedIdents...>(range))
-        return { *ident };
</del><ins>+    auto normalizePercent = [](double percent) {
+        return std::clamp(percent / 100.0, 0.0, 1.0);
+    };
</ins><span class="cx"> 
</span><ins>+    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();
</ins><span class="cx">     return std::nullopt;
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="lines">@@ -767,14 +810,59 @@
</span><span class="cx">     return consumeNumberRaw(range);
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-template<CSSValueID... allowedIdents> static std::optional<Variant<double, CSSValueID>> consumeHueOrIdent(CSSParserTokenRange& range, const CSSParserContext& context)
</del><ins>+static std::optional<double> consumeHueOrIdent(CSSParserTokenRange& range, const CSSCalcSymbolTable& symbolTable)
</ins><span class="cx"> {
</span><del>-    if (auto hue = consumeHue(range, context))
-        return { *hue };
</del><ins>+    const CSSParserToken& token = range.peek();
</ins><span class="cx"> 
</span><del>-    if (auto ident = consumeIdentRaw<allowedIdents...>(range))
-        return { *ident };
</del><ins>+    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();
</ins><span class="cx"> 
</span><ins>+    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();
</ins><span class="cx">     return std::nullopt;
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="lines">@@ -783,66 +871,132 @@
</span><span class="cx">     return std::fmod(std::fmod(hue, 360.0) + 360.0, 360.0);
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-template<CSSValueID... allowedIdents> static std::optional<Variant<double, CSSValueID>> consumeNumberOrIdent(CSSParserTokenRange& range)
</del><ins>+static std::optional<double> consumeNumberOrIdent(CSSParserTokenRange& range, const CSSCalcSymbolTable& symbolTable)
</ins><span class="cx"> {
</span><del>-    if (auto number = consumeNumberRaw(range))
-        return { *number };
</del><ins>+    const CSSParserToken& token = range.peek();
</ins><span class="cx"> 
</span><del>-    if (auto ident = consumeIdentRaw<allowedIdents...>(range))
-        return { *ident };
</del><ins>+    switch (token.type()) {
+    case FunctionToken: {
+        CalcParser calcParser(range, CalculationCategory::Number, ValueRange::All, symbolTable);
+        return calcParser.consumeNumberRaw();
+    }
</ins><span class="cx"> 
</span><ins>+    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();
</ins><span class="cx">     return std::nullopt;
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-template<CSSValueID... allowedIdents> static std::optional<Variant<double, CSSValueID>> consumePercentOrIdent(CSSParserTokenRange& range)
</del><ins>+static std::optional<double> consumePercentOrIdent(CSSParserTokenRange& range, const CSSCalcSymbolTable& symbolTable)
</ins><span class="cx"> {
</span><del>-    if (auto percent = consumePercentRaw(range))
-        return { *percent };
</del><ins>+    const CSSParserToken& token = range.peek();
</ins><span class="cx"> 
</span><del>-    if (auto ident = consumeIdentRaw<allowedIdents...>(range))
-        return { *ident };
</del><ins>+    switch (token.type()) {
+    case FunctionToken: {
+        CalcParser calcParser(range, CalculationCategory::Percent, ValueRange::All, symbolTable);
+        return calcParser.consumePercentRaw();
+    }
</ins><span class="cx"> 
</span><ins>+    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();
</ins><span class="cx">     return std::nullopt;
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-template<CSSValueID C1, CSSValueID C2, CSSValueID C3, CSSValueID AlphaChannel, typename ColorType> static auto extractChannelValue(CSSValueID channel, const ColorType& originColor) -> typename ColorType::ComponentType
</del><ins>+enum class RGBComponentType { Number, Percentage };
+
+static std::optional<uint8_t> consumeNumberOrPercentOrIdentNormalizedForRelativeRGB(CSSParserTokenRange& range, const CSSCalcSymbolTable& symbolTable)
</ins><span class="cx"> {
</span><del>-    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();
</del><ins>+    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;
</ins><span class="cx">     }
</span><span class="cx"> 
</span><del>-    return 0;
-}
</del><ins>+    case PercentageToken:
+        if (std::isinf(token.numericValue()))
+            return std::nullopt;
+        return normalizePercent(range.consumeIncludingWhitespace().numericValue());
</ins><span class="cx"> 
</span><del>-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);
</del><ins>+    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;
+            }
</ins><span class="cx">         }
</span><del>-    );
-}
</del><ins>+        return std::nullopt;
</ins><span class="cx"> 
</span><del>-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; });
</del><ins>+    default:
+        return std::nullopt;
+    }
+
+    ASSERT_NOT_REACHED();
+    return std::nullopt;
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-enum class RGBComponentType { Number, Percentage };
-
</del><span class="cx"> static uint8_t clampRGBComponent(double value, RGBComponentType componentType)
</span><span class="cx"> {
</span><span class="cx">     if (componentType == RGBComponentType::Percentage)
</span><span class="lines">@@ -921,26 +1075,28 @@
</span><span class="cx">     if (!originColor.isValid())
</span><span class="cx">         return { };
</span><span class="cx"> 
</span><del>-    std::optional<RGBComponentType> componentType;
-    auto redResult = consumeRelativeRGBComponent<CSSValueR>(args, componentType);
-    if (!redResult)
</del><ins>+    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)
</ins><span class="cx">         return { };
</span><del>-    auto red = redResult->value;
-    componentType = redResult->type;
</del><span class="cx"> 
</span><del>-    auto greenResult = consumeRelativeRGBComponent<CSSValueG>(args, componentType);
-    if (!greenResult)
</del><ins>+    auto green = consumeNumberOrPercentOrIdentNormalizedForRelativeRGB(args, symbolTable);
+    if (!green)
</ins><span class="cx">         return { };
</span><del>-    auto green = greenResult->value;
-    componentType = greenResult->type;
</del><span class="cx"> 
</span><del>-    auto blueResult = consumeRelativeRGBComponent<CSSValueB>(args, componentType);
-    if (!blueResult)
</del><ins>+    auto blue = consumeNumberOrPercentOrIdentNormalizedForRelativeRGB(args, symbolTable);
+    if (!blue)
</ins><span class="cx">         return { };
</span><del>-    auto blue = blueResult->value;
-    componentType = blueResult->type;
</del><span class="cx"> 
</span><del>-    auto alpha = consumeOptionalAlphaOrIdent<CSSValueAlpha>(args);
</del><ins>+    auto alpha = consumeOptionalAlphaOrIdent(args, symbolTable);
</ins><span class="cx">     if (!alpha)
</span><span class="cx">         return { };
</span><span class="cx"> 
</span><span class="lines">@@ -947,26 +1103,9 @@
</span><span class="cx">     if (!args.atEnd())
</span><span class="cx">         return { };
</span><span class="cx"> 
</span><del>-    // After parsing, convert identifiers to values from the origin color.
</del><ins>+    auto normalizedAlpha = convertFloatAlphaTo<uint8_t>(*alpha);
</ins><span class="cx"> 
</span><del>-    // 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 };
</del><ins>+    return SRGBA<uint8_t> { *red, *green, *blue, normalizedAlpha };
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> enum class RGBFunctionMode { RGB, RGBA };
</span><span class="lines">@@ -1048,19 +1187,28 @@
</span><span class="cx">     if (!originColor.isValid())
</span><span class="cx">         return { };
</span><span class="cx"> 
</span><del>-    auto hue = consumeHueOrIdent<CSSValueH>(args, context);
</del><ins>+    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);
</ins><span class="cx">     if (!hue)
</span><span class="cx">         return { };
</span><span class="cx"> 
</span><del>-    auto saturation = consumePercentOrIdent<CSSValueS>(args);
</del><ins>+    auto saturation = consumePercentOrIdent(args, symbolTable);
</ins><span class="cx">     if (!saturation)
</span><span class="cx">         return { };
</span><span class="cx"> 
</span><del>-    auto lightness = consumePercentOrIdent<CSSValueL>(args);
</del><ins>+    auto lightness = consumePercentOrIdent(args, symbolTable);
</ins><span class="cx">     if (!lightness)
</span><span class="cx">         return { };
</span><span class="cx"> 
</span><del>-    auto alpha = consumeOptionalAlphaOrIdent<CSSValueAlpha>(args);
</del><ins>+    auto alpha = consumeOptionalAlphaOrIdent(args, symbolTable);
</ins><span class="cx">     if (!alpha)
</span><span class="cx">         return { };
</span><span class="cx"> 
</span><span class="lines">@@ -1067,22 +1215,12 @@
</span><span class="cx">     if (!args.atEnd())
</span><span class="cx">         return { };
</span><span class="cx"> 
</span><del>-    // After parsing, convert identifiers to values from the origin color.
</del><ins>+    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);
</ins><span class="cx"> 
</span><del>-    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) });
</del><ins>+    return convertColor<SRGBA<uint8_t>>(HSLA<float> { static_cast<float>(normalizedHue), static_cast<float>(normalizedSaturation), static_cast<float>(normalizedLightness), static_cast<float>(normalizedAlpha) });
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> enum class HSLFunctionMode { HSL, HSLA };
</span><span class="lines">@@ -1122,15 +1260,14 @@
</span><span class="cx">         return { };
</span><span class="cx"> 
</span><span class="cx">     auto normalizedHue = normalizeHue(*hue);
</span><del>-    auto normalizedSaturation = clampTo(*saturation, 0.0, 100.0);
-    auto normalizedLightness = clampTo(*lightness, 0.0, 100.0);
-    auto normalizedAlpha = clampTo(*alpha, 0.0, 1.0);
</del><ins>+    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);
</ins><span class="cx"> 
</span><span class="cx">     return convertColor<SRGBA<uint8_t>>(HSLA<float> { static_cast<float>(normalizedHue), static_cast<float>(normalizedSaturation), static_cast<float>(normalizedLightness), static_cast<float>(normalizedAlpha) });
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-template<typename ComponentType>
-struct WhitenessBlackness {
</del><ins>+template<typename ComponentType> struct WhitenessBlackness {
</ins><span class="cx">     ComponentType whiteness;
</span><span class="cx">     ComponentType blackness;
</span><span class="cx"> };
</span><span class="lines">@@ -1164,19 +1301,28 @@
</span><span class="cx">     if (!originColor.isValid())
</span><span class="cx">         return { };
</span><span class="cx"> 
</span><del>-    auto hue = consumeHueOrIdent<CSSValueH>(args, context);
</del><ins>+    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);
</ins><span class="cx">     if (!hue)
</span><span class="cx">         return { };
</span><span class="cx"> 
</span><del>-    auto whiteness = consumePercentOrIdent<CSSValueW>(args);
</del><ins>+    auto whiteness = consumePercentOrIdent(args, symbolTable);
</ins><span class="cx">     if (!whiteness)
</span><span class="cx">         return { };
</span><span class="cx"> 
</span><del>-    auto blackness = consumePercentOrIdent<CSSValueB>(args);
</del><ins>+    auto blackness = consumePercentOrIdent(args, symbolTable);
</ins><span class="cx">     if (!blackness)
</span><span class="cx">         return { };
</span><span class="cx"> 
</span><del>-    auto alpha = consumeOptionalAlphaOrIdent<CSSValueAlpha>(args);
</del><ins>+    auto alpha = consumeOptionalAlphaOrIdent(args, symbolTable);
</ins><span class="cx">     if (!alpha)
</span><span class="cx">         return { };
</span><span class="cx"> 
</span><span class="lines">@@ -1183,20 +1329,10 @@
</span><span class="cx">     if (!args.atEnd())
</span><span class="cx">         return { };
</span><span class="cx"> 
</span><del>-    // After parsing, convert identifiers to values from the origin color.
</del><ins>+    auto normalizedHue = normalizeHue(*hue);
+    auto [normalizedWhitness, normalizedBlackness] = normalizeWhitenessBlackness(*whiteness, *blackness);
</ins><span class="cx"> 
</span><del>-    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) });
</del><ins>+    return convertColor<SRGBA<uint8_t>>(HWBA<float> { static_cast<float>(normalizedHue), static_cast<float>(normalizedWhitness), static_cast<float>(normalizedBlackness), static_cast<float>(*alpha) });
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> static Color parseHWBParameters(CSSParserTokenRange& range, const CSSParserContext& context)
</span><span class="lines">@@ -1245,19 +1381,28 @@
</span><span class="cx">     if (!originColor.isValid())
</span><span class="cx">         return { };
</span><span class="cx"> 
</span><del>-    auto lightness = consumePercentOrIdent<CSSValueL>(args);
</del><ins>+    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);
</ins><span class="cx">     if (!lightness)
</span><span class="cx">         return { };
</span><span class="cx"> 
</span><del>-    auto aValue = consumeNumberOrIdent<CSSValueA>(args);
</del><ins>+    auto aValue = consumeNumberOrIdent(args, symbolTable);
</ins><span class="cx">     if (!aValue)
</span><span class="cx">         return { };
</span><span class="cx"> 
</span><del>-    auto bValue = consumeNumberOrIdent<CSSValueB>(args);
</del><ins>+    auto bValue = consumeNumberOrIdent(args, symbolTable);
</ins><span class="cx">     if (!bValue)
</span><span class="cx">         return { };
</span><span class="cx"> 
</span><del>-    auto alpha = consumeOptionalAlphaOrIdent<CSSValueAlpha>(args);
</del><ins>+    auto alpha = consumeOptionalAlphaOrIdent(args, symbolTable);
</ins><span class="cx">     if (!alpha)
</span><span class="cx">         return { };
</span><span class="cx"> 
</span><span class="lines">@@ -1264,18 +1409,9 @@
</span><span class="cx">     if (!args.atEnd())
</span><span class="cx">         return { };
</span><span class="cx"> 
</span><del>-    // After parsing, convert identifiers to values from the origin color.
</del><ins>+    auto normalizedLightness = std::max(0.0, *lightness);
</ins><span class="cx"> 
</span><del>-    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) };
</del><ins>+    return Lab<float> { static_cast<float>(normalizedLightness), static_cast<float>(*aValue), static_cast<float>(*bValue), static_cast<float>(*alpha) };
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> static Color parseLabParameters(CSSParserTokenRange& range, const CSSParserContext& context)
</span><span class="lines">@@ -1323,19 +1459,28 @@
</span><span class="cx">     if (!originColor.isValid())
</span><span class="cx">         return { };
</span><span class="cx"> 
</span><del>-    auto lightness = consumePercentOrIdent<CSSValueL>(args);
</del><ins>+    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);
</ins><span class="cx">     if (!lightness)
</span><span class="cx">         return { };
</span><span class="cx"> 
</span><del>-    auto chroma = consumeNumberOrIdent<CSSValueC>(args);
</del><ins>+    auto chroma = consumeNumberOrIdent(args, symbolTable);
</ins><span class="cx">     if (!chroma)
</span><span class="cx">         return { };
</span><span class="cx"> 
</span><del>-    auto hue = consumeHueOrIdent<CSSValueH>(args, context);
</del><ins>+    auto hue = consumeHueOrIdent(args, symbolTable);
</ins><span class="cx">     if (!hue)
</span><span class="cx">         return { };
</span><span class="cx"> 
</span><del>-    auto alpha = consumeOptionalAlphaOrIdent<CSSValueAlpha>(args);
</del><ins>+    auto alpha = consumeOptionalAlphaOrIdent(args, symbolTable);
</ins><span class="cx">     if (!alpha)
</span><span class="cx">         return { };
</span><span class="cx"> 
</span><span class="lines">@@ -1342,20 +1487,11 @@
</span><span class="cx">     if (!args.atEnd())
</span><span class="cx">         return { };
</span><span class="cx"> 
</span><del>-    auto originColorAsLCH = originColor.toColorTypeLossy<LCHA<float>>();
</del><ins>+    auto normalizedLightness = std::max(0.0, *lightness);
+    auto normalizedChroma = std::max(0.0, *chroma);
+    auto normalizedHue = normalizeHue(*hue);
</ins><span class="cx"> 
</span><del>-    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) };
</del><ins>+    return LCHA<float> { static_cast<float>(normalizedLightness), static_cast<float>(normalizedChroma), static_cast<float>(normalizedHue), static_cast<float>(*alpha) };
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> static Color parseLCHParameters(CSSParserTokenRange& range, const CSSParserContext& context)
</span></span></pre>
</div>
</div>

</body>
</html>