Changeset 278261 in webkit


Ignore:
Timestamp:
May 30, 2021 5:29:18 PM (14 months ago)
Author:
weinig@apple.com
Message:

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

Reviewed by Darin Adler.

Source/WebCore:

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

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

This allows for things like:

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

or

background: lch(from rebeccapurple g b r);

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

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

  • Sources.txt:
  • WebCore.xcodeproj/project.pbxproj:
  • css/calc/CSSCalcSymbolTable.cpp: Added.
  • css/calc/CSSCalcSymbolTable.h: Added.

Add CSSCalcSymbolTable which contains a mapping from CSSValueID to CSSUnitType/double pairs.

  • css/calc/CSSCalcExpressionNodeParser.cpp:

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

  • css/calc/CSSCalcExpressionNodeParser.h:

Add reference to a CSSCalcSymbolTable to the parser. The parser should only ever be used
on the stack, so this works well and allows us to avoid copying the table.

  • css/calc/CSSCalcValue.cpp:

(WebCore::CSSCalcValue::create):

  • css/calc/CSSCalcValue.h:

Pass the CSSCalcSymbolTable to the parser if one is provided. An overload was used
to avoid #including CSSCalcSymbolTable.h in the header just to add a default value.

  • css/makevalues.pl:

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

  • css/parser/CSSPropertyParserHelpers.cpp:

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

LayoutTests:

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

  • fast/css/parsing-relative-color-syntax-expected.txt:
  • fast/css/parsing-relative-color-syntax.html:
Location:
trunk
Files:
12 edited
2 copied

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r278245 r278261  
     12021-05-30  Sam Weinig  <weinig@apple.com>
     2
     3        Support calc() on components inside relative color syntax colors
     4        https://bugs.webkit.org/show_bug.cgi?id=226272
     5
     6        Reviewed by Darin Adler.
     7
     8        Updated test and results for update support including calc()
     9        and compoment permutation.
     10
     11        * fast/css/parsing-relative-color-syntax-expected.txt:
     12        * fast/css/parsing-relative-color-syntax.html:
     13
    1142021-05-29  Cameron McCormack  <heycam@apple.com>
    215
  • trunk/LayoutTests/fast/css/parsing-relative-color-syntax-expected.txt

    r273127 r278261  
    4343PASS computedStyle("background-color", "rgb(from rgb(10%, 20%, 30%, 40%) r 25 b / 25%)") is "rgba(26, 25, 77, 0.25)"
    4444PASS computedStyle("background-color", "rgb(from rgb(10%, 20%, 30%, 40%) r g 25 / 25%)") is "rgba(26, 51, 25, 0.25)"
    45 PASS computedStyle("background-color", "rgb(from rebeccapurple g b r)") is "rgba(0, 0, 0, 0)"
    46 PASS computedStyle("background-color", "rgb(from rebeccapurple b alpha r / g)") is "rgba(0, 0, 0, 0)"
    47 PASS computedStyle("background-color", "rgb(from rebeccapurple r r r / r)") is "rgba(0, 0, 0, 0)"
    48 PASS computedStyle("background-color", "rgb(from rebeccapurple alpha alpha alpha / alpha)") is "rgba(0, 0, 0, 0)"
    49 PASS computedStyle("background-color", "rgb(from rgb(10%, 20%, 30%, 40%) g b r)") is "rgba(0, 0, 0, 0)"
    50 PASS computedStyle("background-color", "rgb(from rgb(10%, 20%, 30%, 40%) b alpha r / g)") is "rgba(0, 0, 0, 0)"
    51 PASS computedStyle("background-color", "rgb(from rgb(10%, 20%, 30%, 40%) r r r / r)") is "rgba(0, 0, 0, 0)"
    52 PASS computedStyle("background-color", "rgb(from rgb(10%, 20%, 30%, 40%) alpha alpha alpha / alpha)") is "rgba(0, 0, 0, 0)"
    53 PASS computedStyle("background-color", "rgb(from rebeccapurple r 10% 10)") is "rgba(0, 0, 0, 0)"
    54 PASS computedStyle("background-color", "rgb(from rebeccapurple r 10 10%)") is "rgba(0, 0, 0, 0)"
    55 PASS computedStyle("background-color", "rgb(from rebeccapurple 0% 10 10)") is "rgba(0, 0, 0, 0)"
    56 PASS computedStyle("background-color", "rgb(from rgb(10%, 20%, 30%, 40%) r 10% 10)") is "rgba(0, 0, 0, 0)"
    57 PASS computedStyle("background-color", "rgb(from rgb(10%, 20%, 30%, 40%) r 10 10%)") is "rgba(0, 0, 0, 0)"
    58 PASS computedStyle("background-color", "rgb(from rgb(10%, 20%, 30%, 40%) 0% 10 10)") is "rgba(0, 0, 0, 0)"
     45PASS computedStyle("background-color", "rgb(from rebeccapurple g b r)") is "rgb(51, 153, 102)"
     46PASS computedStyle("background-color", "rgb(from rebeccapurple b alpha r / g)") is "rgba(153, 255, 102, 0.2)"
     47PASS computedStyle("background-color", "rgb(from rebeccapurple r r r / r)") is "rgba(102, 102, 102, 0.4)"
     48PASS computedStyle("background-color", "rgb(from rebeccapurple alpha alpha alpha / alpha)") is "rgb(255, 255, 255)"
     49PASS computedStyle("background-color", "rgb(from rgb(10%, 20%, 30%, 40%) g b r)") is "rgb(51, 77, 26)"
     50PASS computedStyle("background-color", "rgb(from rgb(10%, 20%, 30%, 40%) b alpha r / g)") is "rgba(77, 102, 26, 0.2)"
     51PASS computedStyle("background-color", "rgb(from rgb(10%, 20%, 30%, 40%) r r r / r)") is "rgba(26, 26, 26, 0.1)"
     52PASS computedStyle("background-color", "rgb(from rgb(10%, 20%, 30%, 40%) alpha alpha alpha / alpha)") is "rgba(102, 102, 102, 0.4)"
     53PASS computedStyle("background-color", "rgb(from rebeccapurple r 10% 10)") is "rgb(102, 26, 10)"
     54PASS computedStyle("background-color", "rgb(from rebeccapurple r 10 10%)") is "rgb(102, 10, 26)"
     55PASS computedStyle("background-color", "rgb(from rebeccapurple 0% 10 10)") is "rgb(0, 10, 10)"
     56PASS computedStyle("background-color", "rgb(from rgb(10%, 20%, 30%, 40%) r 10% 10)") is "rgb(26, 26, 10)"
     57PASS computedStyle("background-color", "rgb(from rgb(10%, 20%, 30%, 40%) r 10 10%)") is "rgb(26, 10, 26)"
     58PASS computedStyle("background-color", "rgb(from rgb(10%, 20%, 30%, 40%) 0% 10 10)") is "rgb(0, 10, 10)"
     59PASS computedStyle("background-color", "rgb(from rebeccapurple calc(r) calc(g) calc(b))") is "rgb(102, 51, 153)"
     60PASS computedStyle("background-color", "rgb(from rebeccapurple r calc(g * .5) 10)") is "rgb(102, 26, 10)"
     61PASS computedStyle("background-color", "rgb(from rebeccapurple r calc(b * .5) 10)") is "rgb(102, 77, 10)"
     62PASS computedStyle("background-color", "rgb(from rebeccapurple r calc(b * .5 + g * .5) 10)") is "rgb(102, 102, 10)"
     63PASS computedStyle("background-color", "rgb(from rebeccapurple r calc(b * .5 - g * .5) 10)") is "rgb(102, 51, 10)"
     64PASS computedStyle("background-color", "rgb(from rebeccapurple r calc(b * .5 - g * .5) 10)") is "rgb(102, 51, 10)"
     65PASS 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)"
    5966PASS computedStyle("background-color", "rgb(from rebeccapurple red g b)") is "rgba(0, 0, 0, 0)"
    6067PASS computedStyle("background-color", "rgb(from rebeccapurple l g b)") is "rgba(0, 0, 0, 0)"
     
    9299PASS computedStyle("background-color", "hsl(from rgb(10%, 20%, 30%, 40%) h s 25% / alpha)") is "rgba(32, 63, 95, 0.4)"
    93100PASS computedStyle("background-color", "hsl(from rgb(10%, 20%, 30%, 40%) h s l / .25)") is "rgba(26, 51, 77, 0.25)"
    94 PASS computedStyle("background-color", "hsl(from rebeccapurple h l s)") is "rgba(0, 0, 0, 0)"
    95 PASS computedStyle("background-color", "hsl(from rebeccapurple h alpha l / s)") is "rgba(0, 0, 0, 0)"
    96 PASS computedStyle("background-color", "hsl(from rebeccapurple h l l / l)") is "rgba(0, 0, 0, 0)"
    97 PASS computedStyle("background-color", "hsl(from rebeccapurple h alpha alpha / alpha)") is "rgba(0, 0, 0, 0)"
    98 PASS computedStyle("background-color", "hsl(from rgb(10%, 20%, 30%, 40%) h l s)") is "rgba(0, 0, 0, 0)"
    99 PASS computedStyle("background-color", "hsl(from rgb(10%, 20%, 30%, 40%) h alpha l / s)") is "rgba(0, 0, 0, 0)"
    100 PASS computedStyle("background-color", "hsl(from rgb(10%, 20%, 30%, 40%) h l l / l)") is "rgba(0, 0, 0, 0)"
    101 PASS computedStyle("background-color", "hsl(from rgb(10%, 20%, 30%, 40%) h alpha alpha / alpha)") is "rgba(0, 0, 0, 0)"
     101PASS computedStyle("background-color", "hsl(from rebeccapurple h l s)") is "rgb(128, 77, 179)"
     102PASS computedStyle("background-color", "hsl(from rebeccapurple h alpha l / s)") is "rgba(102, 0, 204, 0.5)"
     103PASS computedStyle("background-color", "hsl(from rebeccapurple h l l / l)") is "rgba(102, 61, 143, 0.4)"
     104PASS computedStyle("background-color", "hsl(from rebeccapurple h alpha alpha / alpha)") is "rgb(255, 255, 255)"
     105PASS computedStyle("background-color", "hsl(from rgb(10%, 20%, 30%, 40%) h l s)") is "rgb(101, 126, 152)"
     106PASS computedStyle("background-color", "hsl(from rgb(10%, 20%, 30%, 40%) h alpha l / s)") is "rgba(31, 51, 72, 0.494)"
     107PASS computedStyle("background-color", "hsl(from rgb(10%, 20%, 30%, 40%) h l l / l)") is "rgba(41, 51, 62, 0.204)"
     108PASS computedStyle("background-color", "hsl(from rgb(10%, 20%, 30%, 40%) h alpha alpha / alpha)") is "rgba(61, 101, 143, 0.4)"
     109PASS computedStyle("background-color", "hsl(from rebeccapurple s h l)") is "rgba(0, 0, 0, 0)"
     110PASS computedStyle("background-color", "hsl(from rebeccapurple s s s / s)") is "rgba(0, 0, 0, 0)"
     111PASS computedStyle("background-color", "hsl(from rebeccapurple h h h / h)") is "rgba(0, 0, 0, 0)"
     112PASS computedStyle("background-color", "hsl(from rebeccapurple alpha alpha alpha / alpha)") is "rgba(0, 0, 0, 0)"
     113PASS computedStyle("background-color", "hsl(from rgb(10%, 20%, 30%, 40%) s h l)") is "rgba(0, 0, 0, 0)"
     114PASS computedStyle("background-color", "hsl(from rgb(10%, 20%, 30%, 40%) s s s / s)") is "rgba(0, 0, 0, 0)"
     115PASS computedStyle("background-color", "hsl(from rgb(10%, 20%, 30%, 40%) h h h / h)") is "rgba(0, 0, 0, 0)"
     116PASS computedStyle("background-color", "hsl(from rgb(10%, 20%, 30%, 40%) alpha alpha alpha / alpha)") is "rgba(0, 0, 0, 0)"
     117PASS computedStyle("background-color", "hsl(from rebeccapurple calc(h) calc(s) calc(l))") is "rgb(102, 51, 153)"
     118PASS 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)"
    102119PASS computedStyle("background-color", "hsl(from rebeccapurple h 10% 10)") is "rgba(0, 0, 0, 0)"
    103120PASS computedStyle("background-color", "hsl(from rebeccapurple h 10 10%)") is "rgba(0, 0, 0, 0)"
     
    141158PASS computedStyle("background-color", "hwb(from rgb(10%, 20%, 30%, 40%) h w 25% / alpha)") is "rgba(26, 107, 191, 0.4)"
    142159PASS computedStyle("background-color", "hwb(from rgb(10%, 20%, 30%, 40%) h w b / .25)") is "rgba(26, 51, 77, 0.25)"
    143 PASS computedStyle("background-color", "hwb(from rebeccapurple h b w)") is "rgba(0, 0, 0, 0)"
    144 PASS computedStyle("background-color", "hwb(from rebeccapurple h alpha w / b)") is "rgba(0, 0, 0, 0)"
    145 PASS computedStyle("background-color", "hwb(from rebeccapurple h w w / w)") is "rgba(0, 0, 0, 0)"
    146 PASS computedStyle("background-color", "hwb(from rebeccapurple h alpha alpha / alpha)") is "rgba(0, 0, 0, 0)"
    147 PASS computedStyle("background-color", "hwb(from rgb(10%, 20%, 30%, 40%) h b w)") is "rgba(0, 0, 0, 0)"
    148 PASS computedStyle("background-color", "hwb(from rgb(10%, 20%, 30%, 40%) h alpha w / b)") is "rgba(0, 0, 0, 0)"
    149 PASS computedStyle("background-color", "hwb(from rgb(10%, 20%, 30%, 40%) h w w / w)") is "rgba(0, 0, 0, 0)"
    150 PASS computedStyle("background-color", "hwb(from rgb(10%, 20%, 30%, 40%) h alpha alpha / alpha)") is "rgba(0, 0, 0, 0)"
     160PASS computedStyle("background-color", "hwb(from rebeccapurple h b w)") is "rgb(153, 102, 204)"
     161PASS computedStyle("background-color", "hwb(from rebeccapurple h alpha w / b)") is "rgba(213, 213, 213, 0.4)"
     162PASS computedStyle("background-color", "hwb(from rebeccapurple h w w / w)") is "rgba(128, 51, 204, 0.2)"
     163PASS computedStyle("background-color", "hwb(from rebeccapurple h alpha alpha / alpha)") is "rgb(128, 128, 128)"
     164PASS computedStyle("background-color", "hwb(from rgb(10%, 20%, 30%, 40%) h b w)") is "rgb(178, 203, 229)"
     165PASS computedStyle("background-color", "hwb(from rgb(10%, 20%, 30%, 40%) h alpha w / b)") is "rgba(102, 164, 229, 0.698)"
     166PASS computedStyle("background-color", "hwb(from rgb(10%, 20%, 30%, 40%) h w w / w)") is "rgba(26, 126, 229, 0.1)"
     167PASS computedStyle("background-color", "hwb(from rgb(10%, 20%, 30%, 40%) h alpha alpha / alpha)") is "rgba(102, 127, 153, 0.4)"
     168PASS computedStyle("background-color", "hwb(from rebeccapurple w h b)") is "rgba(0, 0, 0, 0)"
     169PASS computedStyle("background-color", "hwb(from rebeccapurple b b b / b)") is "rgba(0, 0, 0, 0)"
     170PASS computedStyle("background-color", "hwb(from rebeccapurple h h h / h)") is "rgba(0, 0, 0, 0)"
     171PASS computedStyle("background-color", "hwb(from rebeccapurple alpha alpha alpha / alpha)") is "rgba(0, 0, 0, 0)"
     172PASS computedStyle("background-color", "hwb(from rgb(10%, 20%, 30%, 40%) w b h)") is "rgba(0, 0, 0, 0)"
     173PASS computedStyle("background-color", "hwb(from rgb(10%, 20%, 30%, 40%) b b b / b)") is "rgba(0, 0, 0, 0)"
     174PASS computedStyle("background-color", "hwb(from rgb(10%, 20%, 30%, 40%) h h h / h)") is "rgba(0, 0, 0, 0)"
     175PASS computedStyle("background-color", "hwb(from rgb(10%, 20%, 30%, 40%) alpha alpha alpha / alpha)") is "rgba(0, 0, 0, 0)"
     176PASS computedStyle("background-color", "hwb(from rebeccapurple calc(h) calc(w) calc(b))") is "rgb(102, 51, 153)"
     177PASS 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)"
    151178PASS computedStyle("background-color", "hwb(from rebeccapurple h 10% 10)") is "rgba(0, 0, 0, 0)"
    152179PASS computedStyle("background-color", "hwb(from rebeccapurple h 10 10%)") is "rgba(0, 0, 0, 0)"
     
    183210PASS computedStyle("background-color", "lab(from lab(25% 20 50 / 40%) l a 35 / alpha)") is "lab(25% 20 35 / 0.4)"
    184211PASS computedStyle("background-color", "lab(from lab(25% 20 50 / 40%) l a b / .35)") is "lab(25% 20 50 / 0.35)"
    185 PASS computedStyle("background-color", "lab(from lab(25% 20 50) l b a)") is "rgba(0, 0, 0, 0)"
     212PASS computedStyle("background-color", "lab(from lab(25% 20 50) l b a)") is "lab(25% 50 20)"
     213PASS computedStyle("background-color", "lab(from lab(25% 20 50) l a a / a)") is "lab(25% 20 20)"
     214PASS computedStyle("background-color", "lab(from lab(25% 20 50 / 40%) l b a)") is "lab(25% 50 20)"
     215PASS computedStyle("background-color", "lab(from lab(25% 20 50 / 40%) l a a / a)") is "lab(25% 20 20)"
    186216PASS computedStyle("background-color", "lab(from lab(25% 20 50) l alpha a / b)") is "rgba(0, 0, 0, 0)"
    187 PASS computedStyle("background-color", "lab(from lab(25% 20 50) l a a / a)") is "rgba(0, 0, 0, 0)"
    188217PASS computedStyle("background-color", "lab(from lab(25% 20 50) l alpha alpha / alpha)") is "rgba(0, 0, 0, 0)"
    189 PASS computedStyle("background-color", "lab(from lab(25% 20 50 / 40%) l b a)") is "rgba(0, 0, 0, 0)"
    190218PASS computedStyle("background-color", "lab(from lab(25% 20 50 / 40%) l alpha a / b)") is "rgba(0, 0, 0, 0)"
    191 PASS computedStyle("background-color", "lab(from lab(25% 20 50 / 40%) l a a / a)") is "rgba(0, 0, 0, 0)"
    192219PASS computedStyle("background-color", "lab(from lab(25% 20 50 / 40%) l alpha alpha / alpha)") is "rgba(0, 0, 0, 0)"
     220PASS computedStyle("background-color", "lab(from lab(25% 20 50) calc(l) calc(a) calc(b))") is "lab(25% 20 50)"
     221PASS 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)"
    193222PASS computedStyle("background-color", "lab(from lab(25% 20 50) l 10% 10)") is "rgba(0, 0, 0, 0)"
    194223PASS computedStyle("background-color", "lab(from lab(25% 20 50) l 10 10%)") is "rgba(0, 0, 0, 0)"
     
    232261PASS computedStyle("background-color", "lch(from lch(70% 45 30 / 40%) l c 25deg / alpha)") is "lch(70% 45 25 / 0.4)"
    233262PASS computedStyle("background-color", "lch(from lch(70% 45 30 / 40%) l c h / .25)") is "lch(70% 45 30 / 0.25)"
    234 PASS computedStyle("background-color", "lch(from lch(70% 45 30) alpha c h / l)") is "rgba(0, 0, 0, 0)"
    235 PASS computedStyle("background-color", "lch(from lch(70% 45 30) alpha c h / alpha)") is "rgba(0, 0, 0, 0)"
    236 PASS computedStyle("background-color", "lch(from lch(70% 45 30 / 40%) alpha c h / l)") is "rgba(0, 0, 0, 0)"
    237 PASS computedStyle("background-color", "lch(from lch(70% 45 30 / 40%) alpha c h / alpha)") is "rgba(0, 0, 0, 0)"
     263PASS computedStyle("background-color", "lch(from lch(70% 45 30) alpha c h / l)") is "lch(100% 45 30 / 0.7)"
     264PASS computedStyle("background-color", "lch(from lch(70% 45 30) l c c / alpha)") is "lch(70% 45 45)"
     265PASS computedStyle("background-color", "lch(from lch(70% 45 30) alpha c h / alpha)") is "lch(100% 45 30)"
     266PASS computedStyle("background-color", "lch(from lch(70% 45 30) alpha c c / alpha)") is "lch(100% 45 45)"
     267PASS computedStyle("background-color", "lch(from lch(70% 45 30 / 40%) alpha c h / l)") is "lch(40% 45 30 / 0.7)"
     268PASS computedStyle("background-color", "lch(from lch(70% 45 30 / 40%) l c c / alpha)") is "lch(70% 45 45 / 0.4)"
     269PASS computedStyle("background-color", "lch(from lch(70% 45 30 / 40%) alpha c h / alpha)") is "lch(40% 45 30 / 0.4)"
     270PASS computedStyle("background-color", "lch(from lch(70% 45 30 / 40%) alpha c c / alpha)") is "lch(40% 45 45 / 0.4)"
     271PASS computedStyle("background-color", "lch(from lch(70% 45 30) h l c / alpha)") is "rgba(0, 0, 0, 0)"
     272PASS computedStyle("background-color", "lch(from lch(70% 45 30) c c c / c)") is "rgba(0, 0, 0, 0)"
     273PASS computedStyle("background-color", "lch(from lch(70% 45 30) alpha alpha alpha / alpha)") is "rgba(0, 0, 0, 0)"
     274PASS computedStyle("background-color", "lch(from lch(70% 45 30 / 40%) h l c / alpha)") is "rgba(0, 0, 0, 0)"
     275PASS computedStyle("background-color", "lch(from lch(70% 45 30 / 40%) c c c / c)") is "rgba(0, 0, 0, 0)"
     276PASS computedStyle("background-color", "lch(from lch(70% 45 30 / 40%) alpha alpha alpha / alpha)") is "rgba(0, 0, 0, 0)"
     277PASS computedStyle("background-color", "lch(from lch(70% 45 30) calc(l) calc(c) calc(h))") is "lch(70% 45 30)"
     278PASS 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)"
    238279PASS computedStyle("background-color", "lch(from lch(70% 45 30) l 10% h)") is "rgba(0, 0, 0, 0)"
    239280PASS computedStyle("background-color", "lch(from lch(70% 45 30) l c 10%)") is "rgba(0, 0, 0, 0)"
     
    245286PASS computedStyle("background-color", "lch(from lch(70% 45 30) x c h)") is "rgba(0, 0, 0, 0)"
    246287PASS computedStyle("background-color", "lch(from lch(70% 45 30) l g b)") is "rgba(0, 0, 0, 0)"
     288PASS computedStyle("background-color", "rgb(from var(--bg-color) r g b / 80%)") is "rgba(0, 0, 255, 0.8)"
     289PASS computedStyle("background-color", "lch(from var(--color) calc(l / 2) c h)") is "lch(23.138971% 67.989716 134.39125)"
     290PASS 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)"
     291PASS computedStyle("background-color", "lch(from var(--color) l 0 h)") is "lch(46.277943% 0 134.39125)"
     292PASS computedStyle("background-color", "rgb(from indianred 255 g b)") is "rgb(255, 92, 92)"
     293PASS computedStyle("background-color", "hsl(from var(--accent) calc(h + 180deg) s l)") is "rgb(178, 32, 40)"
     294PASS computedStyle("background-color", "lab(from var(--mycolor) l a b / 100%)") is "lab(62.751923% 52.45802 -34.117283)"
     295PASS computedStyle("background-color", "lab(from var(--mycolor) l a b / calc(alpha * 0.8))") is "lab(62.751923% 52.45802 -34.117283 / 0.8)"
     296PASS computedStyle("background-color", "lab(from var(--mycolor) l a b / calc(alpha - 20%))") is "lab(62.751923% 52.45802 -34.117283 / 0.8)"
     297PASS computedStyle("background-color", "lab(from var(--mycolor) l 0 0)") is "lab(62.751923% 0 0)"
     298PASS computedStyle("background-color", "lch(from peru calc(l * 0.8) c h)") is "lch(49.80138% 54.003296 63.680317)"
     299PASS computedStyle("background-color", "LCH(from var(--accent) l c calc(h + 180deg))") is "lch(65.49473% 39.446903 10.114471)"
     300PASS computedStyle("background-color", "lch(from var(--mycolor) l 0 h)") is "lch(62.751923% 0 326.96112)"
     301PASS computedStyle("background-color", "var(--mygray)") is "lch(62.751923% 0 326.96112)"
     302PASS computedStyle("background-color", "lch(from var(--mygray) l 30 h)") is "lch(62.751923% 30 326.96112)"
    247303PASS successfullyParsed is true
    248304
  • trunk/LayoutTests/fast/css/parsing-relative-color-syntax.html

    r273127 r278261  
    22<html>
    33    <script src="../../resources/js-test-pre.js"></script>
     4    <style>
     5        html {
     6            --bg-color: blue;
     7            --color: green;
     8            --accent: lightseagreen;
     9            --mycolor: orchid;
     10            --mygray: lch(from var(--mycolor) l 0 h);
     11        }
     12    </style>
    413</head>
    514<body>
     
    96105    testComputed(`rgb(from rgb(10%, 20%, 30%, 40%) r g 25 / 25%)`, `rgba(26, 51, 25, 0.25)`);
    97106
    98     // Testing permutation (unclear if this is allowed per-spec, we disallow it).
    99     testComputed(`rgb(from rebeccapurple g b r)`, `rgba(0, 0, 0, 0)`);
    100     testComputed(`rgb(from rebeccapurple b alpha r / g)`, `rgba(0, 0, 0, 0)`);
    101     testComputed(`rgb(from rebeccapurple r r r / r)`, `rgba(0, 0, 0, 0)`);
    102     testComputed(`rgb(from rebeccapurple alpha alpha alpha / alpha)`, `rgba(0, 0, 0, 0)`);
    103     testComputed(`rgb(from rgb(10%, 20%, 30%, 40%) g b r)`, `rgba(0, 0, 0, 0)`);
    104     testComputed(`rgb(from rgb(10%, 20%, 30%, 40%) b alpha r / g)`, `rgba(0, 0, 0, 0)`);
    105     testComputed(`rgb(from rgb(10%, 20%, 30%, 40%) r r r / r)`, `rgba(0, 0, 0, 0)`);
    106     testComputed(`rgb(from rgb(10%, 20%, 30%, 40%) alpha alpha alpha / alpha)`, `rgba(0, 0, 0, 0)`);
    107 
    108     // Testing invalid mixes of number and percentage
    109     testComputed(`rgb(from rebeccapurple r 10% 10)`, `rgba(0, 0, 0, 0)`);
    110     testComputed(`rgb(from rebeccapurple r 10 10%)`, `rgba(0, 0, 0, 0)`);
    111     testComputed(`rgb(from rebeccapurple 0% 10 10)`, `rgba(0, 0, 0, 0)`);
    112     testComputed(`rgb(from rgb(10%, 20%, 30%, 40%) r 10% 10)`, `rgba(0, 0, 0, 0)`);
    113     testComputed(`rgb(from rgb(10%, 20%, 30%, 40%) r 10 10%)`, `rgba(0, 0, 0, 0)`);
    114     testComputed(`rgb(from rgb(10%, 20%, 30%, 40%) 0% 10 10)`, `rgba(0, 0, 0, 0)`);
     107    // Testing permutation.
     108    testComputed(`rgb(from rebeccapurple g b r)`, `rgb(51, 153, 102)`);
     109    testComputed(`rgb(from rebeccapurple b alpha r / g)`, `rgba(153, 255, 102, 0.2)`);
     110    testComputed(`rgb(from rebeccapurple r r r / r)`, `rgba(102, 102, 102, 0.4)`);
     111    testComputed(`rgb(from rebeccapurple alpha alpha alpha / alpha)`, `rgb(255, 255, 255)`);
     112    testComputed(`rgb(from rgb(10%, 20%, 30%, 40%) g b r)`, `rgb(51, 77, 26)`);
     113    testComputed(`rgb(from rgb(10%, 20%, 30%, 40%) b alpha r / g)`, `rgba(77, 102, 26, 0.2)`);
     114    testComputed(`rgb(from rgb(10%, 20%, 30%, 40%) r r r / r)`, `rgba(26, 26, 26, 0.1)`);
     115    testComputed(`rgb(from rgb(10%, 20%, 30%, 40%) alpha alpha alpha / alpha)`, `rgba(102, 102, 102, 0.4)`);
     116
     117    // Testing mixes of number and percentage. (These would not be allowed in the non-relative syntax).
     118    testComputed(`rgb(from rebeccapurple r 10% 10)`, `rgb(102, 26, 10)`);
     119    testComputed(`rgb(from rebeccapurple r 10 10%)`, `rgb(102, 10, 26)`);
     120    testComputed(`rgb(from rebeccapurple 0% 10 10)`, `rgb(0, 10, 10)`);
     121    testComputed(`rgb(from rgb(10%, 20%, 30%, 40%) r 10% 10)`, `rgb(26, 26, 10)`);
     122    testComputed(`rgb(from rgb(10%, 20%, 30%, 40%) r 10 10%)`, `rgb(26, 10, 26)`);
     123    testComputed(`rgb(from rgb(10%, 20%, 30%, 40%) 0% 10 10)`, `rgb(0, 10, 10)`);
     124
     125    // Testing with calc().
     126    testComputed(`rgb(from rebeccapurple calc(r) calc(g) calc(b))`, `rgb(102, 51, 153)`);
     127    testComputed(`rgb(from rebeccapurple r calc(g * .5) 10)`, `rgb(102, 26, 10)`);
     128    testComputed(`rgb(from rebeccapurple r calc(b * .5) 10)`, `rgb(102, 77, 10)`);
     129    testComputed(`rgb(from rebeccapurple r calc(b * .5 + g * .5) 10)`, `rgb(102, 102, 10)`);
     130    testComputed(`rgb(from rebeccapurple r calc(b * .5 - g * .5) 10)`, `rgb(102, 51, 10)`);
     131    testComputed(`rgb(from rebeccapurple r calc(b * .5 - g * .5) 10)`, `rgb(102, 51, 10)`);
     132    testComputed(`rgb(from rgb(10%, 20%, 30%, 40%) calc(r) calc(g) calc(b) / calc(alpha))`, `rgba(26, 51, 77, 0.4)`);
    115133
    116134    // Testing invalid component names
     
    162180    testComputed(`hsl(from rgb(10%, 20%, 30%, 40%) h s l / .25)`, `rgba(26, 51, 77, 0.25)`);
    163181
    164     // Testing permutation (unclear if this is allowed per-spec, we disallow it).
    165     testComputed(`hsl(from rebeccapurple h l s)`, `rgba(0, 0, 0, 0)`);
    166     testComputed(`hsl(from rebeccapurple h alpha l / s)`, `rgba(0, 0, 0, 0)`);
    167     testComputed(`hsl(from rebeccapurple h l l / l)`, `rgba(0, 0, 0, 0)`);
    168     testComputed(`hsl(from rebeccapurple h alpha alpha / alpha)`, `rgba(0, 0, 0, 0)`);
    169     testComputed(`hsl(from rgb(10%, 20%, 30%, 40%) h l s)`, `rgba(0, 0, 0, 0)`);
    170     testComputed(`hsl(from rgb(10%, 20%, 30%, 40%) h alpha l / s)`, `rgba(0, 0, 0, 0)`);
    171     testComputed(`hsl(from rgb(10%, 20%, 30%, 40%) h l l / l)`, `rgba(0, 0, 0, 0)`);
    172     testComputed(`hsl(from rgb(10%, 20%, 30%, 40%) h alpha alpha / alpha)`, `rgba(0, 0, 0, 0)`);
     182    // Testing valid permutation (types match).
     183    testComputed(`hsl(from rebeccapurple h l s)`, `rgb(128, 77, 179)`);
     184    testComputed(`hsl(from rebeccapurple h alpha l / s)`, `rgba(102, 0, 204, 0.5)`);
     185    testComputed(`hsl(from rebeccapurple h l l / l)`, `rgba(102, 61, 143, 0.4)`);
     186    testComputed(`hsl(from rebeccapurple h alpha alpha / alpha)`, `rgb(255, 255, 255)`);
     187    testComputed(`hsl(from rgb(10%, 20%, 30%, 40%) h l s)`, `rgb(101, 126, 152)`);
     188    testComputed(`hsl(from rgb(10%, 20%, 30%, 40%) h alpha l / s)`, `rgba(31, 51, 72, 0.494)`);
     189    testComputed(`hsl(from rgb(10%, 20%, 30%, 40%) h l l / l)`, `rgba(41, 51, 62, 0.204)`);
     190    testComputed(`hsl(from rgb(10%, 20%, 30%, 40%) h alpha alpha / alpha)`, `rgba(61, 101, 143, 0.4)`);
     191
     192    // Testing invalid permutation (types don't match).
     193    testComputed(`hsl(from rebeccapurple s h l)`, `rgba(0, 0, 0, 0)`);
     194    testComputed(`hsl(from rebeccapurple s s s / s)`, `rgba(0, 0, 0, 0)`);
     195    testComputed(`hsl(from rebeccapurple h h h / h)`, `rgba(0, 0, 0, 0)`);
     196    testComputed(`hsl(from rebeccapurple alpha alpha alpha / alpha)`, `rgba(0, 0, 0, 0)`);
     197    testComputed(`hsl(from rgb(10%, 20%, 30%, 40%) s h l)`, `rgba(0, 0, 0, 0)`);
     198    testComputed(`hsl(from rgb(10%, 20%, 30%, 40%) s s s / s)`, `rgba(0, 0, 0, 0)`);
     199    testComputed(`hsl(from rgb(10%, 20%, 30%, 40%) h h h / h)`, `rgba(0, 0, 0, 0)`);
     200    testComputed(`hsl(from rgb(10%, 20%, 30%, 40%) alpha alpha alpha / alpha)`, `rgba(0, 0, 0, 0)`);
     201
     202    // Testing with calc().
     203    testComputed(`hsl(from rebeccapurple calc(h) calc(s) calc(l))`, `rgb(102, 51, 153)`);
     204    testComputed(`hsl(from rgb(10%, 20%, 30%, 40%) calc(h) calc(s) calc(l) / calc(alpha))`, `rgba(26, 51, 77, 0.4)`);
    173205
    174206    // Testing invalid values.
     
    229261    testComputed(`hwb(from rgb(10%, 20%, 30%, 40%) h w b / .25)`, `rgba(26, 51, 77, 0.25)`);
    230262
    231     // Testing permutation (unclear if this is allowed per-spec, we disallow it).
    232     testComputed(`hwb(from rebeccapurple h b w)`, `rgba(0, 0, 0, 0)`);
    233     testComputed(`hwb(from rebeccapurple h alpha w / b)`, `rgba(0, 0, 0, 0)`);
    234     testComputed(`hwb(from rebeccapurple h w w / w)`, `rgba(0, 0, 0, 0)`);
    235     testComputed(`hwb(from rebeccapurple h alpha alpha / alpha)`, `rgba(0, 0, 0, 0)`);
    236     testComputed(`hwb(from rgb(10%, 20%, 30%, 40%) h b w)`, `rgba(0, 0, 0, 0)`);
    237     testComputed(`hwb(from rgb(10%, 20%, 30%, 40%) h alpha w / b)`, `rgba(0, 0, 0, 0)`);
    238     testComputed(`hwb(from rgb(10%, 20%, 30%, 40%) h w w / w)`, `rgba(0, 0, 0, 0)`);
    239     testComputed(`hwb(from rgb(10%, 20%, 30%, 40%) h alpha alpha / alpha)`, `rgba(0, 0, 0, 0)`);
     263    // Testing valid permutation (types match).
     264    testComputed(`hwb(from rebeccapurple h b w)`, `rgb(153, 102, 204)`);
     265    testComputed(`hwb(from rebeccapurple h alpha w / b)`, `rgba(213, 213, 213, 0.4)`);
     266    testComputed(`hwb(from rebeccapurple h w w / w)`, `rgba(128, 51, 204, 0.2)`);
     267    testComputed(`hwb(from rebeccapurple h alpha alpha / alpha)`, `rgb(128, 128, 128)`);
     268    testComputed(`hwb(from rgb(10%, 20%, 30%, 40%) h b w)`, `rgb(178, 203, 229)`);
     269    testComputed(`hwb(from rgb(10%, 20%, 30%, 40%) h alpha w / b)`, `rgba(102, 164, 229, 0.698)`);
     270    testComputed(`hwb(from rgb(10%, 20%, 30%, 40%) h w w / w)`, `rgba(26, 126, 229, 0.1)`);
     271    testComputed(`hwb(from rgb(10%, 20%, 30%, 40%) h alpha alpha / alpha)`, `rgba(102, 127, 153, 0.4)`);
     272
     273    // Testing invalid permutation (types don't match).
     274    testComputed(`hwb(from rebeccapurple w h b)`, `rgba(0, 0, 0, 0)`);
     275    testComputed(`hwb(from rebeccapurple b b b / b)`, `rgba(0, 0, 0, 0)`);
     276    testComputed(`hwb(from rebeccapurple h h h / h)`, `rgba(0, 0, 0, 0)`);
     277    testComputed(`hwb(from rebeccapurple alpha alpha alpha / alpha)`, `rgba(0, 0, 0, 0)`);
     278    testComputed(`hwb(from rgb(10%, 20%, 30%, 40%) w b h)`, `rgba(0, 0, 0, 0)`);
     279    testComputed(`hwb(from rgb(10%, 20%, 30%, 40%) b b b / b)`, `rgba(0, 0, 0, 0)`);
     280    testComputed(`hwb(from rgb(10%, 20%, 30%, 40%) h h h / h)`, `rgba(0, 0, 0, 0)`);
     281    testComputed(`hwb(from rgb(10%, 20%, 30%, 40%) alpha alpha alpha / alpha)`, `rgba(0, 0, 0, 0)`);
     282
     283    // Testing with calc().
     284    testComputed(`hwb(from rebeccapurple calc(h) calc(w) calc(b))`, `rgb(102, 51, 153)`);
     285    testComputed(`hwb(from rgb(10%, 20%, 30%, 40%) calc(h) calc(w) calc(b) / calc(alpha))`, `rgba(26, 51, 77, 0.4)`);
    240286
    241287    // Testing invalid values.
     
    289335    testComputed(`lab(from lab(25% 20 50 / 40%) l a b / .35)`, `lab(25% 20 50 / 0.35)`);
    290336
    291     // Testing permutation (unclear if this is allowed per-spec, we disallow it).
    292     testComputed(`lab(from lab(25% 20 50) l b a)`, `rgba(0, 0, 0, 0)`);
     337    // Testing valid permutation (types match).
     338    testComputed(`lab(from lab(25% 20 50) l b a)`, `lab(25% 50 20)`);
     339    testComputed(`lab(from lab(25% 20 50) l a a / a)`, `lab(25% 20 20)`);
     340    testComputed(`lab(from lab(25% 20 50 / 40%) l b a)`, `lab(25% 50 20)`);
     341    testComputed(`lab(from lab(25% 20 50 / 40%) l a a / a)`, `lab(25% 20 20)`);
     342
     343    // Testing invalid permutation (types don't match).
    293344    testComputed(`lab(from lab(25% 20 50) l alpha a / b)`, `rgba(0, 0, 0, 0)`);
    294     testComputed(`lab(from lab(25% 20 50) l a a / a)`, `rgba(0, 0, 0, 0)`);
    295345    testComputed(`lab(from lab(25% 20 50) l alpha alpha / alpha)`, `rgba(0, 0, 0, 0)`);
    296     testComputed(`lab(from lab(25% 20 50 / 40%) l b a)`, `rgba(0, 0, 0, 0)`);
    297346    testComputed(`lab(from lab(25% 20 50 / 40%) l alpha a / b)`, `rgba(0, 0, 0, 0)`);
    298     testComputed(`lab(from lab(25% 20 50 / 40%) l a a / a)`, `rgba(0, 0, 0, 0)`);
    299347    testComputed(`lab(from lab(25% 20 50 / 40%) l alpha alpha / alpha)`, `rgba(0, 0, 0, 0)`);
     348
     349    // Testing with calc().
     350    testComputed(`lab(from lab(25% 20 50) calc(l) calc(a) calc(b))`, `lab(25% 20 50)`);
     351    testComputed(`lab(from lab(25% 20 50 / 40%) calc(l) calc(a) calc(b) / calc(alpha))`, `lab(25% 20 50 / 0.4)`);
    300352
    301353    // Testing invalid values.
     
    356408    testComputed(`lch(from lch(70% 45 30 / 40%) l c h / .25)`, `lch(70% 45 30 / 0.25)`);
    357409
    358     // Testing permutation (unclear if this is allowed per-spec, we disallow it).
    359     testComputed(`lch(from lch(70% 45 30) alpha c h / l)`, `rgba(0, 0, 0, 0)`);
    360     testComputed(`lch(from lch(70% 45 30) alpha c h / alpha)`, `rgba(0, 0, 0, 0)`);
    361     testComputed(`lch(from lch(70% 45 30 / 40%) alpha c h / l)`, `rgba(0, 0, 0, 0)`);
    362     testComputed(`lch(from lch(70% 45 30 / 40%) alpha c h / alpha)`, `rgba(0, 0, 0, 0)`);
     410    // Testing valid permutation (types match).
     411    // NOTE: 'c' is a vaild hue, as hue is <angle>|<number>.
     412    testComputed(`lch(from lch(70% 45 30) alpha c h / l)`, `lch(100% 45 30 / 0.7)`);
     413    testComputed(`lch(from lch(70% 45 30) l c c / alpha)`, `lch(70% 45 45)`);
     414    testComputed(`lch(from lch(70% 45 30) alpha c h / alpha)`, `lch(100% 45 30)`);
     415    testComputed(`lch(from lch(70% 45 30) alpha c c / alpha)`, `lch(100% 45 45)`);
     416    testComputed(`lch(from lch(70% 45 30 / 40%) alpha c h / l)`, `lch(40% 45 30 / 0.7)`);
     417    testComputed(`lch(from lch(70% 45 30 / 40%) l c c / alpha)`, `lch(70% 45 45 / 0.4)`);
     418    testComputed(`lch(from lch(70% 45 30 / 40%) alpha c h / alpha)`, `lch(40% 45 30 / 0.4)`);
     419    testComputed(`lch(from lch(70% 45 30 / 40%) alpha c c / alpha)`, `lch(40% 45 45 / 0.4)`);
     420
     421    // Testing invalid permutation (types don't match).
     422    testComputed(`lch(from lch(70% 45 30) h l c / alpha)`, `rgba(0, 0, 0, 0)`);
     423    testComputed(`lch(from lch(70% 45 30) c c c / c)`, `rgba(0, 0, 0, 0)`);
     424    testComputed(`lch(from lch(70% 45 30) alpha alpha alpha / alpha)`, `rgba(0, 0, 0, 0)`);
     425    testComputed(`lch(from lch(70% 45 30 / 40%) h l c / alpha)`, `rgba(0, 0, 0, 0)`);
     426    testComputed(`lch(from lch(70% 45 30 / 40%) c c c / c)`, `rgba(0, 0, 0, 0)`);
     427    testComputed(`lch(from lch(70% 45 30 / 40%) alpha alpha alpha / alpha)`, `rgba(0, 0, 0, 0)`);
     428
     429    // Testing with calc().
     430    testComputed(`lch(from lch(70% 45 30) calc(l) calc(c) calc(h))`, `lch(70% 45 30)`);
     431    testComputed(`lch(from lch(70% 45 30 / 40%) calc(l) calc(c) calc(h) / calc(alpha))`, `lch(70% 45 30 / 0.4)`);
    363432
    364433    // Testing invalid values.
     
    374443    testComputed(`lch(from lch(70% 45 30) x c h)`, `rgba(0, 0, 0, 0)`);
    375444    testComputed(`lch(from lch(70% 45 30) l g b)`, `rgba(0, 0, 0, 0)`);
     445
     446
     447    // Spec examples:
     448    // Example 11.
     449    testComputed(`rgb(from var(--bg-color) r g b / 80%)`, `rgba(0, 0, 255, 0.8)`);
     450   
     451    // Example 12.
     452    testComputed(`lch(from var(--color) calc(l / 2) c h)`, `lch(23.138971% 67.989716 134.39125)`);
     453   
     454    // Example 13.
     455    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)`)
     456    testComputed(`lch(from var(--color) l 0 h)`, `lch(46.277943% 0 134.39125)`)
     457
     458    // Example 14.
     459    testComputed(`rgb(from indianred 255 g b)`, `rgb(255, 92, 92)`);
     460
     461    // Example 15.
     462    testComputed(`hsl(from var(--accent) calc(h + 180deg) s l)`, `rgb(178, 32, 40)`);
     463
     464    // Example 16.
     465    testComputed(`lab(from var(--mycolor) l a b / 100%)`, `lab(62.751923% 52.45802 -34.117283)`);
     466    testComputed(`lab(from var(--mycolor) l a b / calc(alpha * 0.8))`, `lab(62.751923% 52.45802 -34.117283 / 0.8)`);
     467    testComputed(`lab(from var(--mycolor) l a b / calc(alpha - 20%))`, `lab(62.751923% 52.45802 -34.117283 / 0.8)`);
     468   
     469    // Example 17.
     470    testComputed(`lab(from var(--mycolor) l 0 0)`, `lab(62.751923% 0 0)`);
     471
     472    // Example 18.
     473    testComputed(`lch(from peru calc(l * 0.8) c h)`, `lch(49.80138% 54.003296 63.680317)`);
     474
     475    // Example 19.
     476    testComputed(`LCH(from var(--accent) l c calc(h + 180deg))`, `lch(65.49473% 39.446903 10.114471)`);
     477
     478    // Example 20.
     479    testComputed(`lch(from var(--mycolor) l 0 h)`, `lch(62.751923% 0 326.96112)`);
     480    testComputed(`var(--mygray)`, `lch(62.751923% 0 326.96112)`);
     481    testComputed(`lch(from var(--mygray) l 30 h)`, `lch(62.751923% 30 326.96112)`);
     482   
    376483</script>
    377484   
  • trunk/Source/WebCore/ChangeLog

    r278260 r278261  
     12021-05-30  Sam Weinig  <weinig@apple.com>
     2
     3        Support calc() on components inside relative color syntax colors
     4        https://bugs.webkit.org/show_bug.cgi?id=226272
     5
     6        Reviewed by Darin Adler.
     7
     8        Added new and updated test cases to fast/css/parsing-relative-color-syntax.html.
     9
     10        Updates support for the CSS Color 5 "Relative Color Syntax" to support
     11        both calc() on components and component permutations within the syntax.
     12
     13        This allows for things like:
     14       
     15            background: lch(from var(--primary-color) 60% calc(c * 0.8) h);
     16
     17            or
     18
     19            background: lch(from rebeccapurple g b r);
     20
     21        To make this work, the calc() infrastructure now supports passing a CSSCalcSymbolTable
     22        which allows the logic in the parser to lookup unknown identifiers when parsing a value.
     23        The relative color syntax parsers can then build an appropriate CSSCalcSymbolTable filled
     24        with the components of the origin color.
     25
     26        Since these calc() values are not serialized, this can all happen in the parser, but if
     27        we to be able serialize them in the future, CSSCalcPrimitiveValueNode could be updated
     28        to store the symbol name in addition to storing the value and type (or we could we could
     29        add a new node for it).
     30
     31        * Sources.txt:
     32        * WebCore.xcodeproj/project.pbxproj:
     33        * css/calc/CSSCalcSymbolTable.cpp: Added.
     34        * css/calc/CSSCalcSymbolTable.h: Added.
     35        Add CSSCalcSymbolTable which contains a mapping from CSSValueID to CSSUnitType/double pairs.
     36
     37        * css/calc/CSSCalcExpressionNodeParser.cpp:
     38        (WebCore::CSSCalcExpressionNodeParser::parseValue):
     39        When trying to construct a value node, if the token is a identifier, use the new
     40        symbol table to look up a type/value to use instead. Use a switch to make things
     41        a bit more clear that differentiation is being done based on token type.
     42
     43        * css/calc/CSSCalcExpressionNodeParser.h:
     44        Add reference to a CSSCalcSymbolTable to the parser. The parser should only ever be used
     45        on the stack, so this works well and allows us to avoid copying the table.
     46
     47        * css/calc/CSSCalcValue.cpp:
     48        (WebCore::CSSCalcValue::create):
     49        * css/calc/CSSCalcValue.h:
     50        Pass the CSSCalcSymbolTable to the parser if one is provided. An overload was used
     51        to avoid #including CSSCalcSymbolTable.h in the header just to add a default value.
     52
     53        * css/makevalues.pl:
     54        Give a concrete base of uint16_t to allow it to be forward declared and add DefaultHash
     55        and HashTraits to allow it to be used with HashTable. These match the definition of
     56        CSSPropertyID.
     57
     58        * css/parser/CSSPropertyParserHelpers.cpp:
     59        (WebCore::CSSPropertyParserHelpers::CalcParser::CalcParser):
     60        (WebCore::CSSPropertyParserHelpers::consumeNumberRaw):
     61        (WebCore::CSSPropertyParserHelpers::consumePercentWorkerSafe):
     62        (WebCore::CSSPropertyParserHelpers::consumeAngleRaw):
     63        (WebCore::CSSPropertyParserHelpers::consumeAngleWorkerSafe):
     64        (WebCore::CSSPropertyParserHelpers::consumeAngleOrPercent):
     65        (WebCore::CSSPropertyParserHelpers::consumeOptionalAlphaOrIdent):
     66        (WebCore::CSSPropertyParserHelpers::consumeHueOrIdent):
     67        (WebCore::CSSPropertyParserHelpers::consumeNumberOrIdent):
     68        (WebCore::CSSPropertyParserHelpers::consumePercentOrIdent):
     69        (WebCore::CSSPropertyParserHelpers::consumeNumberOrPercentOrIdentNormalizedForRelativeRGB):
     70        (WebCore::CSSPropertyParserHelpers::parseRelativeRGBParameters):
     71        (WebCore::CSSPropertyParserHelpers::parseRelativeHSLParameters):
     72        (WebCore::CSSPropertyParserHelpers::parseRelativeHWBParameters):
     73        (WebCore::CSSPropertyParserHelpers::parseRelativeLabParameters):
     74        (WebCore::CSSPropertyParserHelpers::parseRelativeLCHParameters):
     75        (WebCore::CSSPropertyParserHelpers::extractChannelValue): Deleted.
     76        (WebCore::CSSPropertyParserHelpers::resolveRelativeColorChannel): Deleted.
     77        Rework relative color syntax parsing to allow permutation of channel symbols
     78        and use of the calc() symbol table support to allow passing in the channels.
     79        This makes the relatative color syntax variants much closer to the normal
     80        variants (with the exception of passing the symbol table) and a subsequent
     81        change will attempt to merge them further.
     82
    1832021-05-30  Dean Jackson  <dino@apple.com>
    284
  • trunk/Source/WebCore/Sources.txt

    r278246 r278261  
    832832css/calc/CSSCalcOperationNode.cpp
    833833css/calc/CSSCalcPrimitiveValueNode.cpp
     834css/calc/CSSCalcSymbolTable.cpp
    834835css/calc/CSSCalcValue.cpp
    835836css/parser/CSSAtRuleID.cpp
  • trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj

    r278246 r278261  
    40784078                BC6D44ED0C07F2ED0072D2C9 /* JSHTMLEmbedElement.h in Headers */ = {isa = PBXBuildFile; fileRef = BC6D44EB0C07F2ED0072D2C9 /* JSHTMLEmbedElement.h */; };
    40794079                BC6D6E2609AF943500F59759 /* ScrollView.h in Headers */ = {isa = PBXBuildFile; fileRef = BC6D6E2509AF943500F59759 /* ScrollView.h */; settings = {ATTRIBUTES = (Private, ); }; };
     4080                BC709DE0266323CF00B9A21C /* CSSCalcSymbolTable.h in Headers */ = {isa = PBXBuildFile; fileRef = BC709DDE266323CE00B9A21C /* CSSCalcSymbolTable.h */; };
    40804081                BC772B3C0C4EA91E0083285F /* CSSHelper.h in Headers */ = {isa = PBXBuildFile; fileRef = BC772B360C4EA91E0083285F /* CSSHelper.h */; settings = {ATTRIBUTES = (Private, ); }; };
    40814082                BC772C470C4EB2C60083285F /* XMLHttpRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = BC772C450C4EB2C60083285F /* XMLHttpRequest.h */; };
     
    1430514306                BC6D6E2509AF943500F59759 /* ScrollView.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = ScrollView.h; sourceTree = "<group>"; };
    1430614307                BC6EB84526266B61003225A7 /* ColorLuminance.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ColorLuminance.cpp; sourceTree = "<group>"; };
     14308                BC709DDE266323CE00B9A21C /* CSSCalcSymbolTable.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CSSCalcSymbolTable.h; sourceTree = "<group>"; };
     14309                BC709DDF266323CE00B9A21C /* CSSCalcSymbolTable.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = CSSCalcSymbolTable.cpp; sourceTree = "<group>"; };
    1430714310                BC772B360C4EA91E0083285F /* CSSHelper.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = CSSHelper.h; sourceTree = "<group>"; };
    1430814311                BC772C440C4EB2C60083285F /* XMLHttpRequest.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = XMLHttpRequest.cpp; sourceTree = "<group>"; };
     
    2795727960                                BCF164542662A1220002F7EF /* CSSCalcPrimitiveValueNode.cpp */,
    2795827961                                BCF164532662A1220002F7EF /* CSSCalcPrimitiveValueNode.h */,
     27962                                BC709DDF266323CE00B9A21C /* CSSCalcSymbolTable.cpp */,
     27963                                BC709DDE266323CE00B9A21C /* CSSCalcSymbolTable.h */,
    2795927964                                49AE2D8C134EE50C0072920A /* CSSCalcValue.cpp */,
    2796027965                                49AE2D8D134EE50C0072920A /* CSSCalcValue.h */,
     
    3467034675                                BCAA487014A052530088FAC4 /* PlatformEventFactoryMac.h in Headers */,
    3467134676                                A723F77B1484CA4C008C6DBE /* PlatformExportMacros.h in Headers */,
     34677                                BC709DE0266323CF00B9A21C /* CSSCalcSymbolTable.h in Headers */,
    3467234678                                515BE1951D54F5FB00DD7C68 /* PlatformGamepad.h in Headers */,
    3467334679                                726D56E2253AE28D0002EF90 /* PlatformImage.h in Headers */,
  • trunk/Source/WebCore/css/calc/CSSCalcExpressionNodeParser.cpp

    r278253 r278261  
    3232#include "CSSCalcOperationNode.h"
    3333#include "CSSCalcPrimitiveValueNode.h"
     34#include "CSSCalcSymbolTable.h"
    3435#include "CSSCalcValue.h"
    3536#include "CSSParserToken.h"
    3637#include "CSSParserTokenRange.h"
     38#include "CSSValueKeywords.h"
    3739#include "Logging.h"
    3840
     
    181183bool CSSCalcExpressionNodeParser::parseValue(CSSParserTokenRange& tokens, RefPtr<CSSCalcExpressionNode>& result)
    182184{
    183     // FIXME: Add code here to parse CSSValidID for named constants.
    184 
    185     CSSParserToken token = tokens.consumeIncludingWhitespace();
    186     if (!(token.type() == NumberToken || token.type() == PercentageToken || token.type() == DimensionToken))
    187         return false;
    188    
    189     auto type = token.unitType();
    190     if (calcUnitCategory(type) == CalculationCategory::Other)
    191         return false;
    192    
    193     result = CSSCalcPrimitiveValueNode::create(CSSPrimitiveValue::create(token.numericValue(), type));
    194    
    195     return true;
     185    auto makeCSSCalcPrimitiveValueNode = [&] (CSSUnitType type, double value) -> bool {
     186        if (calcUnitCategory(type) == CalculationCategory::Other)
     187            return false;
     188       
     189        result = CSSCalcPrimitiveValueNode::create(CSSPrimitiveValue::create(value, type));
     190        return true;
     191    };
     192
     193    auto token = tokens.consumeIncludingWhitespace();
     194
     195    switch (token.type()) {
     196    case IdentToken: {
     197        auto value = m_symbolTable.get(token.id());
     198        if (!value)
     199            return false;
     200        return makeCSSCalcPrimitiveValueNode(value->type, value->value);
     201    }
     202
     203    case NumberToken:
     204    case PercentageToken:
     205    case DimensionToken:
     206        return makeCSSCalcPrimitiveValueNode(token.unitType(), token.numericValue());
     207
     208    default:
     209        return false;
     210    }
     211
     212    ASSERT_NOT_REACHED();
     213    return false;
    196214}
    197215
  • trunk/Source/WebCore/css/calc/CSSCalcExpressionNodeParser.h

    r278246 r278261  
    2626#pragma once
    2727
    28 #include "CSSValueKeywords.h"
    2928#include "CalcOperator.h"
    3029#include "CalculationCategory.h"
     
    3332
    3433class CSSCalcExpressionNode;
     34class CSSCalcSymbolTable;
    3535class CSSParserToken;
    3636class CSSParserTokenRange;
    3737
     38enum CSSValueID : uint16_t;
     39
    3840class CSSCalcExpressionNodeParser {
    3941public:
    40     explicit CSSCalcExpressionNodeParser(CalculationCategory destinationCategory)
     42    explicit CSSCalcExpressionNodeParser(CalculationCategory destinationCategory, const CSSCalcSymbolTable& symbolTable)
    4143        : m_destinationCategory(destinationCategory)
     44        , m_symbolTable(symbolTable)
    4245    {
    4346    }
     
    5659
    5760    CalculationCategory m_destinationCategory;
     61    const CSSCalcSymbolTable& m_symbolTable;
    5862};
    5963
  • trunk/Source/WebCore/css/calc/CSSCalcSymbolTable.cpp

    r278260 r278261  
    2424 */
    2525
    26 #pragma once
     26#include "config.h"
     27#include "CSSCalcSymbolTable.h"
    2728
    28 #include "CSSValueKeywords.h"
    29 #include "CalcOperator.h"
    30 #include "CalculationCategory.h"
     29#include "CSSUnits.h"
    3130
    3231namespace WebCore {
    3332
    34 class CSSCalcExpressionNode;
    35 class CSSParserToken;
    36 class CSSParserTokenRange;
     33CSSCalcSymbolTable::CSSCalcSymbolTable(std::initializer_list<std::tuple<CSSValueID, CSSUnitType, double>> initializer)
     34{
     35    for (auto& [identifier, type, value] : initializer)
     36        m_table.add(identifier, std::make_pair(type, value));
     37}
    3738
    38 class CSSCalcExpressionNodeParser {
    39 public:
    40     explicit CSSCalcExpressionNodeParser(CalculationCategory destinationCategory)
    41         : m_destinationCategory(destinationCategory)
    42     {
    43     }
     39std::optional<CSSCalcSymbolTable::Value> CSSCalcSymbolTable::get(CSSValueID valueID) const
     40{
     41    auto it = m_table.find(valueID);
     42    if (it == m_table.end())
     43        return std::nullopt;
    4444
    45     RefPtr<CSSCalcExpressionNode> parseCalc(CSSParserTokenRange, CSSValueID function);
    46    
    47 private:
    48     char operatorValue(const CSSParserToken&);
    49 
    50     bool parseValue(CSSParserTokenRange&, RefPtr<CSSCalcExpressionNode>&);
    51     bool parseValueTerm(CSSParserTokenRange&, int depth, RefPtr<CSSCalcExpressionNode>&);
    52     bool parseCalcFunction(CSSParserTokenRange&, CSSValueID, int depth, RefPtr<CSSCalcExpressionNode>&);
    53     bool parseCalcSum(CSSParserTokenRange&, int depth, RefPtr<CSSCalcExpressionNode>&);
    54     bool parseCalcProduct(CSSParserTokenRange&, int depth, RefPtr<CSSCalcExpressionNode>&);
    55     bool parseCalcValue(CSSParserTokenRange&, int depth, RefPtr<CSSCalcExpressionNode>&);
    56 
    57     CalculationCategory m_destinationCategory;
    58 };
     45    return {{ it->value.first, it->value.second }};
     46}
    5947
    6048}
  • trunk/Source/WebCore/css/calc/CSSCalcSymbolTable.h

    r278260 r278261  
    2727
    2828#include "CSSValueKeywords.h"
    29 #include "CalcOperator.h"
    30 #include "CalculationCategory.h"
     29#include <wtf/HashMap.h>
     30#include <wtf/Optional.h>
    3131
    3232namespace WebCore {
    3333
    34 class CSSCalcExpressionNode;
    35 class CSSParserToken;
    36 class CSSParserTokenRange;
     34enum class CSSUnitType : uint8_t;
    3735
    38 class CSSCalcExpressionNodeParser {
     36class CSSCalcSymbolTable {
    3937public:
    40     explicit CSSCalcExpressionNodeParser(CalculationCategory destinationCategory)
    41         : m_destinationCategory(destinationCategory)
    42     {
    43     }
     38    struct Value {
     39        CSSUnitType type;
     40        double value;
     41    };
    4442
    45     RefPtr<CSSCalcExpressionNode> parseCalc(CSSParserTokenRange, CSSValueID function);
    46    
     43    CSSCalcSymbolTable() = default;
     44    CSSCalcSymbolTable(std::initializer_list<std::tuple<CSSValueID, CSSUnitType, double>>);
     45
     46    std::optional<Value> get(CSSValueID) const;
     47
    4748private:
    48     char operatorValue(const CSSParserToken&);
    49 
    50     bool parseValue(CSSParserTokenRange&, RefPtr<CSSCalcExpressionNode>&);
    51     bool parseValueTerm(CSSParserTokenRange&, int depth, RefPtr<CSSCalcExpressionNode>&);
    52     bool parseCalcFunction(CSSParserTokenRange&, CSSValueID, int depth, RefPtr<CSSCalcExpressionNode>&);
    53     bool parseCalcSum(CSSParserTokenRange&, int depth, RefPtr<CSSCalcExpressionNode>&);
    54     bool parseCalcProduct(CSSParserTokenRange&, int depth, RefPtr<CSSCalcExpressionNode>&);
    55     bool parseCalcValue(CSSParserTokenRange&, int depth, RefPtr<CSSCalcExpressionNode>&);
    56 
    57     CalculationCategory m_destinationCategory;
     49    HashMap<CSSValueID, std::pair<CSSUnitType, double>> m_table;
    5850};
    5951
    60 }
     52};
  • trunk/Source/WebCore/css/calc/CSSCalcValue.cpp

    r278246 r278261  
    4141#include "CSSParserTokenRange.h"
    4242#include "CSSPrimitiveValueMappings.h"
     43#include "CSSValueKeywords.h"
    4344#include "CalcExpressionBlendLength.h"
    4445#include "CalcExpressionLength.h"
     
    300301}
    301302
    302 RefPtr<CSSCalcValue> CSSCalcValue::create(CSSValueID function, const CSSParserTokenRange& tokens, CalculationCategory destinationCategory, ValueRange range)
    303 {
    304     CSSCalcExpressionNodeParser parser(destinationCategory);
     303RefPtr<CSSCalcValue> CSSCalcValue::create(CSSValueID function, const CSSParserTokenRange& tokens, CalculationCategory destinationCategory, ValueRange range, const CSSCalcSymbolTable& symbolTable)
     304{
     305    CSSCalcExpressionNodeParser parser(destinationCategory, symbolTable);
    305306    auto expression = parser.parseCalc(tokens, function);
    306307    if (!expression)
     
    310311    return result;
    311312}
    312    
     313
     314RefPtr<CSSCalcValue> CSSCalcValue::create(CSSValueID function, const CSSParserTokenRange& tokens, CalculationCategory destinationCategory, ValueRange range)
     315{
     316    return create(function, tokens, destinationCategory, range, { });
     317}
     318
    313319RefPtr<CSSCalcValue> CSSCalcValue::create(const CalculationValue& value, const RenderStyle& style)
    314320{
  • trunk/Source/WebCore/css/calc/CSSCalcValue.h

    r278246 r278261  
    3232
    3333#include "CSSValue.h"
    34 #include "CSSValueKeywords.h"
    3534#include "CalculationValue.h"
    3635#include <wtf/Forward.h>
     
    4039
    4140class CSSCalcExpressionNode;
     41class CSSCalcSymbolTable;
    4242class CSSParserTokenRange;
    4343class CSSToLengthConversionData;
     
    4848enum class ValueRange : uint8_t;
    4949
     50enum CSSValueID : uint16_t;
     51
    5052class CSSCalcValue final : public CSSValue {
    5153public:
     54    static RefPtr<CSSCalcValue> create(CSSValueID function, const CSSParserTokenRange&, CalculationCategory destinationCategory, ValueRange, const CSSCalcSymbolTable&);
    5255    static RefPtr<CSSCalcValue> create(CSSValueID function, const CSSParserTokenRange&, CalculationCategory destinationCategory, ValueRange);
    5356    static RefPtr<CSSCalcValue> create(const CalculationValue&, const RenderStyle&);
  • trunk/Source/WebCore/css/makevalues.pl

    r246636 r278261  
    165165#include <string.h>
    166166#include <wtf/Forward.h>
     167#include <wtf/HashFunctions.h>
     168#include <wtf/HashTraits.h>
    167169
    168170namespace WebCore {
    169171
    170 enum CSSValueID {
     172enum CSSValueID : uint16_t {
    171173    CSSValueInvalid = 0,
    172174EOF
     
    205207
    206208} // namespace WebCore
     209
     210namespace WTF {
     211template<> struct DefaultHash<WebCore::CSSValueID> : IntHash<unsigned> { };
     212template<> struct HashTraits<WebCore::CSSValueID> : StrongEnumHashTraits<WebCore::CSSValueID> { };
     213} // namespace WTF
     214
    207215EOF
    208216close HEADER;
  • trunk/Source/WebCore/css/parser/CSSPropertyParserHelpers.cpp

    r278253 r278261  
    3131#include "CSSPropertyParserHelpers.h"
    3232
     33#include "CSSCalcSymbolTable.h"
    3334#include "CSSCalcValue.h"
    3435#include "CSSCanvasValue.h"
     
    99100class CalcParser {
    100101public:
    101     explicit CalcParser(CSSParserTokenRange& range, CalculationCategory destinationCategory, ValueRange valueRange = ValueRange::All, CSSValuePool& cssValuePool = CSSValuePool::singleton())
     102    explicit CalcParser(CSSParserTokenRange& range, CalculationCategory destinationCategory, ValueRange valueRange = ValueRange::All, const CSSCalcSymbolTable& symbolTable = { }, CSSValuePool& cssValuePool = CSSValuePool::singleton())
    102103        : m_sourceRange(range)
    103104        , m_range(range)
     
    107108        auto functionId = token.functionId();
    108109        if (CSSCalcValue::isCalcFunction(functionId))
    109             m_calcValue = CSSCalcValue::create(functionId, consumeFunction(m_range), destinationCategory, valueRange);
     110            m_calcValue = CSSCalcValue::create(functionId, consumeFunction(m_range), destinationCategory, valueRange, symbolTable);
    110111    }
    111112
     
    115116    {
    116117        if (!m_calcValue)
     118            return nullptr;
     119        m_sourceRange = m_range;
     120        return m_valuePool.createValue(WTFMove(m_calcValue));
     121    }
     122
     123    RefPtr<CSSPrimitiveValue> consumeValueIfCategory(CalculationCategory category)
     124    {
     125        if (!m_calcValue || m_calcValue->category() != category)
    117126            return nullptr;
    118127        m_sourceRange = m_range;
     
    218227
    219228    CalcParser calcParser(range, CalculationCategory::Number);
    220     if (const CSSCalcValue* calculation = calcParser.value()) {
    221         if (calculation->category() != CalculationCategory::Number)
    222             return std::nullopt;
     229    if (auto calculation = calcParser.value(); calculation && calculation->category() == CalculationCategory::Number)
    223230        return calcParser.consumeIntegerTypeRaw<IntType>(minimumValue);
    224     }
    225231
    226232    return std::nullopt;
     
    273279    if (token.type() == FunctionToken) {
    274280        CalcParser calcParser(range, CalculationCategory::Number, valueRange);
    275         if (const auto* calcValue = calcParser.value()) {
    276             if (calcValue->category() == CalculationCategory::Number)
    277                 return calcParser.consumeValue();
    278         }
    279         return nullptr;
     281        return calcParser.consumeValueIfCategory(CalculationCategory::Number);
    280282    }
    281283
     
    399401    if (token.type() == FunctionToken) {
    400402        CalcParser calcParser(range, CalculationCategory::Length, valueRange);
    401         if (calcParser.value() && calcParser.value()->category() == CalculationCategory::Length)
    402             return calcParser.consumeValue();
     403        return calcParser.consumeValueIfCategory(CalculationCategory::Length);
    403404    }
    404405
     
    434435    const CSSParserToken& token = range.peek();
    435436    if (token.type() == FunctionToken) {
    436         CalcParser calcParser(range, CalculationCategory::Percent, valueRange, cssValuePool);
    437         if (const CSSCalcValue* calculation = calcParser.value()) {
    438             if (calculation->category() == CalculationCategory::Percent)
    439                 return calcParser.consumeValue();
    440         }
    441         return nullptr;
     437        CalcParser calcParser(range, CalculationCategory::Percent, valueRange, { }, cssValuePool);
     438        return calcParser.consumeValueIfCategory(CalculationCategory::Percent);
    442439    }
    443440
     
    480477
    481478    CalcParser calcParser(range, CalculationCategory::Length, valueRange);
    482     if (const CSSCalcValue* calculation = calcParser.value()) {
    483         if (canConsumeCalcValue(calculation->category(), cssParserMode))
    484             return calcParser.consumeLengthOrPercentRaw();
    485     }
     479    if (auto calculation = calcParser.value(); calculation && canConsumeCalcValue(calculation->category(), cssParserMode))
     480        return calcParser.consumeLengthOrPercentRaw();
    486481    return std::nullopt;
    487482}
     
    492487    if (token.type() == FunctionToken) {
    493488        CalcParser calcParser(range, CalculationCategory::Length, valueRange);
    494         if (const CSSCalcValue* calculation = calcParser.value()) {
    495             if (canConsumeCalcValue(calculation->category(), cssParserMode))
    496                 return calcParser.consumeValue();
    497         }
     489        if (auto calculation = calcParser.value(); calculation && canConsumeCalcValue(calculation->category(), cssParserMode))
     490            return calcParser.consumeValue();
    498491        return nullptr;
    499492    }
     
    544537    const CSSParserToken& token = range.peek();
    545538    if (token.type() == FunctionToken) {
    546         CalcParser calcParser(range, CalculationCategory::Angle, ValueRange::All, cssValuePool);
    547         if (const CSSCalcValue* calculation = calcParser.value()) {
    548             if (calculation->category() == CalculationCategory::Angle)
    549                 return calcParser.consumeValue();
    550         }
    551         return nullptr;
     539        CalcParser calcParser(range, CalculationCategory::Angle, ValueRange::All, { }, cssValuePool);
     540        return calcParser.consumeValueIfCategory(CalculationCategory::Angle);
    552541    }
    553542
     
    561550{
    562551    const CSSParserToken& token = range.peek();
    563     if (token.type() == DimensionToken) {
     552    switch (token.type()) {
     553    case DimensionToken:
    564554        switch (token.unitType()) {
    565555        case CSSUnitType::CSS_DEG:
     
    569559            return CSSValuePool::singleton().createValue(range.consumeIncludingWhitespace().numericValue(), token.unitType());
    570560        default:
    571             return nullptr;
    572         }
    573     }
    574     if (token.type() == NumberToken && shouldAcceptUnitlessValue(token.numericValue(), cssParserMode, unitless, unitlessZero))
    575         return CSSValuePool::singleton().createValue(range.consumeIncludingWhitespace().numericValue(), CSSUnitType::CSS_DEG);
    576 
    577     if (token.type() == PercentageToken)
     561            break;
     562        }
     563        break;
     564   
     565    case NumberToken:
     566        if (shouldAcceptUnitlessValue(token.numericValue(), cssParserMode, unitless, unitlessZero))
     567            return CSSValuePool::singleton().createValue(range.consumeIncludingWhitespace().numericValue(), CSSUnitType::CSS_DEG);
     568        break;
     569
     570    case PercentageToken:
    578571        return consumePercent(range, valueRange);
    579572
    580      if (token.type() != FunctionToken)
    581          return nullptr;
    582 
    583     CalcParser angleCalcParser(range, CalculationCategory::Angle, valueRange);
    584     if (const CSSCalcValue* calculation = angleCalcParser.value()) {
    585         if (calculation->category() == CalculationCategory::Angle)
    586             return angleCalcParser.consumeValue();
    587     }
    588 
    589     CalcParser percentCalcParser(range, CalculationCategory::Percent, valueRange);
    590     if (const CSSCalcValue* calculation = percentCalcParser.value()) {
    591         if (calculation->category() == CalculationCategory::Percent)
    592             return percentCalcParser.consumeValue();
    593     }
     573    case FunctionToken: {
     574        CalcParser angleCalcParser(range, CalculationCategory::Angle, valueRange);
     575        if (const CSSCalcValue* calculation = angleCalcParser.value()) {
     576            if (calculation->category() == CalculationCategory::Angle)
     577                return angleCalcParser.consumeValue();
     578        }
     579
     580        CalcParser percentCalcParser(range, CalculationCategory::Percent, valueRange);
     581        if (const CSSCalcValue* calculation = percentCalcParser.value()) {
     582            if (calculation->category() == CalculationCategory::Percent)
     583                return percentCalcParser.consumeValue();
     584        }
     585   
     586        break;
     587    }
     588   
     589    default:
     590        break;
     591    }
     592
    594593    return nullptr;
    595594}
     
    614613
    615614    CalcParser calcParser(range, CalculationCategory::Time, valueRange);
    616     if (const CSSCalcValue* calculation = calcParser.value()) {
    617         if (calculation->category() == CalculationCategory::Time)
    618             return calcParser.consumeValue();
    619     }
    620     return nullptr;
     615    return calcParser.consumeValueIfCategory(CalculationCategory::Time);
    621616}
    622617
     
    744739
    745740    if (auto alphaParameter = consumeNumberOrPercentDividedBy100Raw(range))
    746         return clampTo(*alphaParameter, 0.0, 1.0);
     741        return std::clamp(*alphaParameter, 0.0, 1.0);
    747742
    748743    return std::nullopt;
    749744}
    750745
    751 template<CSSValueID... allowedIdents> static std::optional<Variant<double, CSSValueID>> consumeOptionalAlphaOrIdent(CSSParserTokenRange& range)
    752 {
    753     if (auto alpha = consumeOptionalAlpha(range))
    754         return { *alpha };
    755 
    756     if (auto ident = consumeIdentRaw<allowedIdents...>(range))
    757         return { *ident };
    758 
     746static std::optional<double> consumeOptionalAlphaOrIdent(CSSParserTokenRange& range, const CSSCalcSymbolTable& symbolTable)
     747{
     748    if (!consumeSlashIncludingWhitespace(range))
     749        return 1.0;
     750
     751    auto normalizePercent = [](double percent) {
     752        return std::clamp(percent / 100.0, 0.0, 1.0);
     753    };
     754
     755    auto normalizeNumber = [](double alpha) {
     756        return std::clamp(alpha, 0.0, 1.0);
     757    };
     758
     759    const CSSParserToken& token = range.peek();
     760
     761    switch (token.type()) {
     762    case FunctionToken: {
     763        CalcParser percentageCalcParser(range, CalculationCategory::Percent, ValueRange::All, symbolTable);
     764        if (auto percent = percentageCalcParser.consumePercentRaw())
     765            return normalizePercent(*percent);
     766       
     767        CalcParser numberCalcParser(range, CalculationCategory::Number, ValueRange::All, symbolTable);
     768        if (auto number = numberCalcParser.consumeNumberRaw())
     769            return normalizeNumber(*number);
     770
     771        return std::nullopt;
     772    }
     773
     774    case PercentageToken:
     775        if (std::isinf(token.numericValue()))
     776            return std::nullopt;
     777        return normalizePercent(range.consumeIncludingWhitespace().numericValue());
     778
     779    case NumberToken:
     780        return normalizeNumber(range.consumeIncludingWhitespace().numericValue());
     781
     782    case IdentToken:
     783        if (auto variable = symbolTable.get(range.consumeIncludingWhitespace().id())) {
     784            switch (variable->type) {
     785            case CSSUnitType::CSS_PERCENTAGE:
     786                return normalizePercent(variable->value);
     787
     788            case CSSUnitType::CSS_NUMBER:
     789                return normalizeNumber(variable->value);
     790
     791            default:
     792                return std::nullopt;
     793            }
     794        }
     795        return std::nullopt;
     796
     797    default:
     798        return std::nullopt;
     799    }
     800
     801    ASSERT_NOT_REACHED();
    759802    return std::nullopt;
    760803}
     
    768811}
    769812
    770 template<CSSValueID... allowedIdents> static std::optional<Variant<double, CSSValueID>> consumeHueOrIdent(CSSParserTokenRange& range, const CSSParserContext& context)
    771 {
    772     if (auto hue = consumeHue(range, context))
    773         return { *hue };
    774 
    775     if (auto ident = consumeIdentRaw<allowedIdents...>(range))
    776         return { *ident };
    777 
     813static std::optional<double> consumeHueOrIdent(CSSParserTokenRange& range, const CSSCalcSymbolTable& symbolTable)
     814{
     815    const CSSParserToken& token = range.peek();
     816
     817    switch (token.type()) {
     818    case FunctionToken: {
     819        CalcParser angleCalcParser(range, CalculationCategory::Angle, ValueRange::All, symbolTable);
     820        if (auto angle = angleCalcParser.consumeAngleRaw())
     821            return CSSPrimitiveValue::computeDegrees(angle->type, angle->value);
     822       
     823        CalcParser numberCalcParser(range, CalculationCategory::Number, ValueRange::All, symbolTable);
     824        return numberCalcParser.consumeNumberRaw();
     825    }
     826   
     827    case DimensionToken: {
     828        auto unitType = token.unitType();
     829        switch (unitType) {
     830        case CSSUnitType::CSS_DEG:
     831        case CSSUnitType::CSS_RAD:
     832        case CSSUnitType::CSS_GRAD:
     833        case CSSUnitType::CSS_TURN:
     834            return CSSPrimitiveValue::computeDegrees(unitType, range.consumeIncludingWhitespace().numericValue());
     835        default:
     836            return std::nullopt;
     837        }
     838    }
     839   
     840    case NumberToken:
     841        return range.consumeIncludingWhitespace().numericValue();
     842
     843    case IdentToken:
     844        if (auto variable = symbolTable.get(range.consumeIncludingWhitespace().id())) {
     845            switch (variable->type) {
     846            case CSSUnitType::CSS_DEG:
     847            case CSSUnitType::CSS_RAD:
     848            case CSSUnitType::CSS_GRAD:
     849            case CSSUnitType::CSS_TURN:
     850                return CSSPrimitiveValue::computeDegrees(variable->type, variable->value);
     851
     852            case CSSUnitType::CSS_NUMBER:
     853                return variable->value;
     854
     855            default:
     856                return std::nullopt;
     857            }
     858        }
     859        return std::nullopt;
     860
     861    default:
     862        return std::nullopt;
     863    }
     864
     865    ASSERT_NOT_REACHED();
    778866    return std::nullopt;
    779867}
     
    784872}
    785873
    786 template<CSSValueID... allowedIdents> static std::optional<Variant<double, CSSValueID>> consumeNumberOrIdent(CSSParserTokenRange& range)
    787 {
    788     if (auto number = consumeNumberRaw(range))
    789         return { *number };
    790 
    791     if (auto ident = consumeIdentRaw<allowedIdents...>(range))
    792         return { *ident };
    793 
     874static std::optional<double> consumeNumberOrIdent(CSSParserTokenRange& range, const CSSCalcSymbolTable& symbolTable)
     875{
     876    const CSSParserToken& token = range.peek();
     877
     878    switch (token.type()) {
     879    case FunctionToken: {
     880        CalcParser calcParser(range, CalculationCategory::Number, ValueRange::All, symbolTable);
     881        return calcParser.consumeNumberRaw();
     882    }
     883
     884    case NumberToken:
     885        return range.consumeIncludingWhitespace().numericValue();
     886
     887    case IdentToken:
     888        if (auto variable = symbolTable.get(range.consumeIncludingWhitespace().id())) {
     889            switch (variable->type) {
     890            case CSSUnitType::CSS_NUMBER:
     891                return variable->value;
     892
     893            default:
     894                return std::nullopt;
     895            }
     896        }
     897        return std::nullopt;
     898
     899    default:
     900        return std::nullopt;
     901    }
     902
     903    ASSERT_NOT_REACHED();
    794904    return std::nullopt;
    795905}
    796906
    797 template<CSSValueID... allowedIdents> static std::optional<Variant<double, CSSValueID>> consumePercentOrIdent(CSSParserTokenRange& range)
    798 {
    799     if (auto percent = consumePercentRaw(range))
    800         return { *percent };
    801 
    802     if (auto ident = consumeIdentRaw<allowedIdents...>(range))
    803         return { *ident };
    804 
     907static std::optional<double> consumePercentOrIdent(CSSParserTokenRange& range, const CSSCalcSymbolTable& symbolTable)
     908{
     909    const CSSParserToken& token = range.peek();
     910
     911    switch (token.type()) {
     912    case FunctionToken: {
     913        CalcParser calcParser(range, CalculationCategory::Percent, ValueRange::All, symbolTable);
     914        return calcParser.consumePercentRaw();
     915    }
     916
     917    case PercentageToken:
     918        if (std::isinf(token.numericValue()))
     919            return std::nullopt;
     920        return range.consumeIncludingWhitespace().numericValue();
     921
     922    case IdentToken:
     923        if (auto variable = symbolTable.get(range.consumeIncludingWhitespace().id())) {
     924            switch (variable->type) {
     925            case CSSUnitType::CSS_PERCENTAGE:
     926                return variable->value;
     927
     928            default:
     929                return std::nullopt;
     930            }
     931        }
     932        return std::nullopt;
     933
     934    default:
     935        return std::nullopt;
     936    }
     937
     938    ASSERT_NOT_REACHED();
    805939    return std::nullopt;
    806940}
    807941
    808 template<CSSValueID C1, CSSValueID C2, CSSValueID C3, CSSValueID AlphaChannel, typename ColorType> static auto extractChannelValue(CSSValueID channel, const ColorType& originColor) -> typename ColorType::ComponentType
    809 {
    810     auto components = asColorComponents(originColor);
    811     switch (channel) {
    812     case C1:
    813         return components[0];
    814     case C2:
    815         return components[1];
    816     case C3:
    817         return components[2];
    818     case AlphaChannel:
    819         return components[3];
     942enum class RGBComponentType { Number, Percentage };
     943
     944static std::optional<uint8_t> consumeNumberOrPercentOrIdentNormalizedForRelativeRGB(CSSParserTokenRange& range, const CSSCalcSymbolTable& symbolTable)
     945{
     946    auto normalizePercent = [](double percent) {
     947        return convertPrescaledSRGBAFloatToSRGBAByte(percent / 100.0 * 255.0);
     948    };
     949
     950    auto normalizeNumber = [](double number) {
     951        return convertPrescaledSRGBAFloatToSRGBAByte(number);
     952    };
     953
     954    const CSSParserToken& token = range.peek();
     955
     956    switch (token.type()) {
     957    case FunctionToken: {
     958        CalcParser percentageCalcParser(range, CalculationCategory::Percent, ValueRange::All, symbolTable);
     959        if (auto percent = percentageCalcParser.consumePercentRaw())
     960            return normalizePercent(*percent);
     961       
     962        CalcParser numberCalcParser(range, CalculationCategory::Number, ValueRange::All, symbolTable);
     963        if (auto number = numberCalcParser.consumeNumberRaw())
     964            return normalizeNumber(*number);
     965       
     966        return std::nullopt;
     967    }
     968
     969    case PercentageToken:
     970        if (std::isinf(token.numericValue()))
     971            return std::nullopt;
     972        return normalizePercent(range.consumeIncludingWhitespace().numericValue());
     973
     974    case NumberToken:
     975        return normalizeNumber(range.consumeIncludingWhitespace().numericValue());
     976
     977    case IdentToken:
     978        if (auto variable = symbolTable.get(range.consumeIncludingWhitespace().id())) {
     979            switch (variable->type) {
     980            case CSSUnitType::CSS_PERCENTAGE:
     981                return normalizePercent(variable->value);
     982
     983            case CSSUnitType::CSS_NUMBER:
     984                return normalizeNumber(variable->value);
     985
     986            default:
     987                return std::nullopt;
     988            }
     989        }
     990        return std::nullopt;
     991
    820992    default:
    821         ASSERT_NOT_REACHED();
    822     }
    823 
    824     return 0;
    825 }
    826 
    827 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)
    828 {
    829     return switchOn(parsedChannel,
    830         [&] (double value) {
    831             return valueTransformer(value);
    832         },
    833         [&] (CSSValueID channel) {
    834             return extractChannelValue<C1, C2, C3, AlphaChannel>(channel, originColor);
    835         }
    836     );
    837 }
    838 
    839 template<CSSValueID C1, CSSValueID C2, CSSValueID C3, CSSValueID AlphaChannel, typename ColorType> static decltype(auto) resolveRelativeColorChannel(const Variant<double, CSSValueID>& parsedChannel, const ColorType& originColor)
    840 {
    841     return resolveRelativeColorChannel<C1, C2, C3, AlphaChannel>(parsedChannel, originColor, [](auto value) { return value; });
    842 }
    843 
    844 enum class RGBComponentType { Number, Percentage };
     993        return std::nullopt;
     994    }
     995
     996    ASSERT_NOT_REACHED();
     997    return std::nullopt;
     998}
    845999
    8461000static uint8_t clampRGBComponent(double value, RGBComponentType componentType)
     
    9221076        return { };
    9231077
    924     std::optional<RGBComponentType> componentType;
    925     auto redResult = consumeRelativeRGBComponent<CSSValueR>(args, componentType);
    926     if (!redResult)
    927         return { };
    928     auto red = redResult->value;
    929     componentType = redResult->type;
    930 
    931     auto greenResult = consumeRelativeRGBComponent<CSSValueG>(args, componentType);
    932     if (!greenResult)
    933         return { };
    934     auto green = greenResult->value;
    935     componentType = greenResult->type;
    936 
    937     auto blueResult = consumeRelativeRGBComponent<CSSValueB>(args, componentType);
    938     if (!blueResult)
    939         return { };
    940     auto blue = blueResult->value;
    941     componentType = blueResult->type;
    942 
    943     auto alpha = consumeOptionalAlphaOrIdent<CSSValueAlpha>(args);
     1078    auto originColorAsSRGB = originColor.toColorTypeLossy<SRGBA<float>>();
     1079
     1080    CSSCalcSymbolTable symbolTable {
     1081        { CSSValueR, CSSUnitType::CSS_PERCENTAGE, originColorAsSRGB.red * 100.0 },
     1082        { CSSValueG, CSSUnitType::CSS_PERCENTAGE, originColorAsSRGB.green * 100.0 },
     1083        { CSSValueB, CSSUnitType::CSS_PERCENTAGE, originColorAsSRGB.blue * 100.0 },
     1084        { CSSValueAlpha, CSSUnitType::CSS_PERCENTAGE, originColorAsSRGB.alpha * 100.0 }
     1085    };
     1086
     1087    auto red = consumeNumberOrPercentOrIdentNormalizedForRelativeRGB(args, symbolTable);
     1088    if (!red)
     1089        return { };
     1090
     1091    auto green = consumeNumberOrPercentOrIdentNormalizedForRelativeRGB(args, symbolTable);
     1092    if (!green)
     1093        return { };
     1094
     1095    auto blue = consumeNumberOrPercentOrIdentNormalizedForRelativeRGB(args, symbolTable);
     1096    if (!blue)
     1097        return { };
     1098
     1099    auto alpha = consumeOptionalAlphaOrIdent(args, symbolTable);
    9441100    if (!alpha)
    9451101        return { };
     
    9481104        return { };
    9491105
    950     // After parsing, convert identifiers to values from the origin color.
    951 
    952     // FIXME: Do we want to being doing this in uint8_t values? Or should we use
    953     // higher precision and clamp at the end? It won't make a difference until we
    954     // support calculations on the origin's components.
    955     auto originColorAsSRGB = originColor.toSRGBALossy<uint8_t>();
    956 
    957     auto resolvedComponentType = componentType.value_or(RGBComponentType::Percentage);
    958     auto channelResolver = [resolvedComponentType](auto value) {
    959         return clampRGBComponent(value, resolvedComponentType);
    960     };
    961 
    962     auto resolvedRed = resolveRelativeColorChannel<CSSValueR, CSSValueG, CSSValueB, CSSValueAlpha>(red, originColorAsSRGB, channelResolver);
    963     auto resolvedGreen = resolveRelativeColorChannel<CSSValueR, CSSValueG, CSSValueB, CSSValueAlpha>(green, originColorAsSRGB, channelResolver);
    964     auto resolvedBlue = resolveRelativeColorChannel<CSSValueR, CSSValueG, CSSValueB, CSSValueAlpha>(blue, originColorAsSRGB, channelResolver);
    965     auto resolvedAlpha = resolveRelativeColorChannel<CSSValueR, CSSValueG, CSSValueB, CSSValueAlpha>(*alpha, originColorAsSRGB, [](auto value) {
    966         return convertFloatAlphaTo<uint8_t>(value);
    967     });
    968 
    969     return SRGBA<uint8_t> { resolvedRed, resolvedGreen, resolvedBlue, resolvedAlpha };
     1106    auto normalizedAlpha = convertFloatAlphaTo<uint8_t>(*alpha);
     1107
     1108    return SRGBA<uint8_t> { *red, *green, *blue, normalizedAlpha };
    9701109}
    9711110
     
    10491188        return { };
    10501189
    1051     auto hue = consumeHueOrIdent<CSSValueH>(args, context);
     1190    auto originColorAsHSL = originColor.toColorTypeLossy<HSLA<float>>();
     1191
     1192    CSSCalcSymbolTable symbolTable {
     1193        { CSSValueH, CSSUnitType::CSS_DEG, originColorAsHSL.hue },
     1194        { CSSValueS, CSSUnitType::CSS_PERCENTAGE, originColorAsHSL.saturation },
     1195        { CSSValueL, CSSUnitType::CSS_PERCENTAGE, originColorAsHSL.lightness },
     1196        { CSSValueAlpha, CSSUnitType::CSS_PERCENTAGE, originColorAsHSL.alpha * 100.0 }
     1197    };
     1198
     1199    auto hue = consumeHueOrIdent(args, symbolTable);
    10521200    if (!hue)
    10531201        return { };
    10541202
    1055     auto saturation = consumePercentOrIdent<CSSValueS>(args);
     1203    auto saturation = consumePercentOrIdent(args, symbolTable);
    10561204    if (!saturation)
    10571205        return { };
    10581206
    1059     auto lightness = consumePercentOrIdent<CSSValueL>(args);
     1207    auto lightness = consumePercentOrIdent(args, symbolTable);
    10601208    if (!lightness)
    10611209        return { };
    10621210
    1063     auto alpha = consumeOptionalAlphaOrIdent<CSSValueAlpha>(args);
     1211    auto alpha = consumeOptionalAlphaOrIdent(args, symbolTable);
    10641212    if (!alpha)
    10651213        return { };
     
    10681216        return { };
    10691217
    1070     // After parsing, convert identifiers to values from the origin color.
    1071 
    1072     auto originColorAsHSL = originColor.toColorTypeLossy<HSLA<float>>();
    1073 
    1074     auto resolvedHue = resolveRelativeColorChannel<CSSValueH, CSSValueS, CSSValueL, CSSValueAlpha>(*hue, originColorAsHSL, [](auto hue) {
    1075         return normalizeHue(hue);
    1076     });
    1077     auto resolvedSaturation = resolveRelativeColorChannel<CSSValueH, CSSValueS, CSSValueL, CSSValueAlpha>(*saturation, originColorAsHSL, [](auto saturation) {
    1078         return clampTo(saturation, 0.0, 100.0);
    1079     });
    1080     auto resolvedLightness = resolveRelativeColorChannel<CSSValueH, CSSValueS, CSSValueL, CSSValueAlpha>(*lightness, originColorAsHSL, [](auto lightness) {
    1081         return clampTo(lightness, 0.0, 100.0);
    1082     });
    1083     auto resolvedAlpha = resolveRelativeColorChannel<CSSValueH, CSSValueS, CSSValueL, CSSValueAlpha>(*alpha, originColorAsHSL);
    1084 
    1085     return convertColor<SRGBA<uint8_t>>(HSLA<float> { static_cast<float>(resolvedHue), static_cast<float>(resolvedSaturation), static_cast<float>(resolvedLightness), static_cast<float>(resolvedAlpha) });
     1218    auto normalizedHue = normalizeHue(*hue);
     1219    auto normalizedSaturation = std::clamp(*saturation, 0.0, 100.0);
     1220    auto normalizedLightness = std::clamp(*lightness, 0.0, 100.0);
     1221    auto normalizedAlpha = std::clamp(*alpha, 0.0, 1.0);
     1222
     1223    return convertColor<SRGBA<uint8_t>>(HSLA<float> { static_cast<float>(normalizedHue), static_cast<float>(normalizedSaturation), static_cast<float>(normalizedLightness), static_cast<float>(normalizedAlpha) });
    10861224}
    10871225
     
    11231261
    11241262    auto normalizedHue = normalizeHue(*hue);
    1125     auto normalizedSaturation = clampTo(*saturation, 0.0, 100.0);
    1126     auto normalizedLightness = clampTo(*lightness, 0.0, 100.0);
    1127     auto normalizedAlpha = clampTo(*alpha, 0.0, 1.0);
     1263    auto normalizedSaturation = std::clamp(*saturation, 0.0, 100.0);
     1264    auto normalizedLightness = std::clamp(*lightness, 0.0, 100.0);
     1265    auto normalizedAlpha = std::clamp(*alpha, 0.0, 1.0);
    11281266
    11291267    return convertColor<SRGBA<uint8_t>>(HSLA<float> { static_cast<float>(normalizedHue), static_cast<float>(normalizedSaturation), static_cast<float>(normalizedLightness), static_cast<float>(normalizedAlpha) });
    11301268}
    11311269
    1132 template<typename ComponentType>
    1133 struct WhitenessBlackness {
     1270template<typename ComponentType> struct WhitenessBlackness {
    11341271    ComponentType whiteness;
    11351272    ComponentType blackness;
     
    11651302        return { };
    11661303
    1167     auto hue = consumeHueOrIdent<CSSValueH>(args, context);
     1304    auto originColorAsHWB = originColor.toColorTypeLossy<HWBA<float>>();
     1305
     1306    CSSCalcSymbolTable symbolTable {
     1307        { CSSValueH, CSSUnitType::CSS_DEG, originColorAsHWB.hue },
     1308        { CSSValueW, CSSUnitType::CSS_PERCENTAGE, originColorAsHWB.whiteness },
     1309        { CSSValueB, CSSUnitType::CSS_PERCENTAGE, originColorAsHWB.blackness },
     1310        { CSSValueAlpha, CSSUnitType::CSS_PERCENTAGE, originColorAsHWB.alpha * 100.0 }
     1311    };
     1312
     1313    auto hue = consumeHueOrIdent(args, symbolTable);
    11681314    if (!hue)
    11691315        return { };
    11701316
    1171     auto whiteness = consumePercentOrIdent<CSSValueW>(args);
     1317    auto whiteness = consumePercentOrIdent(args, symbolTable);
    11721318    if (!whiteness)
    11731319        return { };
    11741320
    1175     auto blackness = consumePercentOrIdent<CSSValueB>(args);
     1321    auto blackness = consumePercentOrIdent(args, symbolTable);
    11761322    if (!blackness)
    11771323        return { };
    11781324
    1179     auto alpha = consumeOptionalAlphaOrIdent<CSSValueAlpha>(args);
     1325    auto alpha = consumeOptionalAlphaOrIdent(args, symbolTable);
    11801326    if (!alpha)
    11811327        return { };
     
    11841330        return { };
    11851331
    1186     // After parsing, convert identifiers to values from the origin color.
    1187 
    1188     auto originColorAsHWB = originColor.toColorTypeLossy<HWBA<float>>();
    1189 
    1190     auto resolvedHue = resolveRelativeColorChannel<CSSValueH, CSSValueW, CSSValueB, CSSValueAlpha>(*hue, originColorAsHWB, [](auto hue) {
    1191         return normalizeHue(hue);
    1192     });
    1193     auto resolvedWhiteness = resolveRelativeColorChannel<CSSValueH, CSSValueW, CSSValueB, CSSValueAlpha>(*whiteness, originColorAsHWB);
    1194     auto resolvedBlackness = resolveRelativeColorChannel<CSSValueH, CSSValueW, CSSValueB, CSSValueAlpha>(*blackness, originColorAsHWB);
    1195     auto resolvedAlpha = resolveRelativeColorChannel<CSSValueH, CSSValueW, CSSValueB, CSSValueAlpha>(*alpha, originColorAsHWB);
    1196 
    1197     auto [normalizedWhitness, normalizedBlackness] = normalizeWhitenessBlackness(resolvedWhiteness, resolvedBlackness);
    1198 
    1199     return convertColor<SRGBA<uint8_t>>(HWBA<float> { static_cast<float>(resolvedHue), static_cast<float>(normalizedWhitness), static_cast<float>(normalizedBlackness), static_cast<float>(resolvedAlpha) });
     1332    auto normalizedHue = normalizeHue(*hue);
     1333    auto [normalizedWhitness, normalizedBlackness] = normalizeWhitenessBlackness(*whiteness, *blackness);
     1334
     1335    return convertColor<SRGBA<uint8_t>>(HWBA<float> { static_cast<float>(normalizedHue), static_cast<float>(normalizedWhitness), static_cast<float>(normalizedBlackness), static_cast<float>(*alpha) });
    12001336}
    12011337
     
    12461382        return { };
    12471383
    1248     auto lightness = consumePercentOrIdent<CSSValueL>(args);
     1384    auto originColorAsLab = originColor.toColorTypeLossy<Lab<float>>();
     1385
     1386    CSSCalcSymbolTable symbolTable {
     1387        { CSSValueL, CSSUnitType::CSS_PERCENTAGE, originColorAsLab.lightness },
     1388        { CSSValueA, CSSUnitType::CSS_NUMBER, originColorAsLab.a },
     1389        { CSSValueB, CSSUnitType::CSS_NUMBER, originColorAsLab.b },
     1390        { CSSValueAlpha, CSSUnitType::CSS_PERCENTAGE, originColorAsLab.alpha * 100.0 }
     1391    };
     1392
     1393    auto lightness = consumePercentOrIdent(args, symbolTable);
    12491394    if (!lightness)
    12501395        return { };
    12511396
    1252     auto aValue = consumeNumberOrIdent<CSSValueA>(args);
     1397    auto aValue = consumeNumberOrIdent(args, symbolTable);
    12531398    if (!aValue)
    12541399        return { };
    12551400
    1256     auto bValue = consumeNumberOrIdent<CSSValueB>(args);
     1401    auto bValue = consumeNumberOrIdent(args, symbolTable);
    12571402    if (!bValue)
    12581403        return { };
    12591404
    1260     auto alpha = consumeOptionalAlphaOrIdent<CSSValueAlpha>(args);
     1405    auto alpha = consumeOptionalAlphaOrIdent(args, symbolTable);
    12611406    if (!alpha)
    12621407        return { };
     
    12651410        return { };
    12661411
    1267     // After parsing, convert identifiers to values from the origin color.
    1268 
    1269     auto originColorAsLab = originColor.toColorTypeLossy<Lab<float>>();
    1270 
    1271     auto resolvedLightness = resolveRelativeColorChannel<CSSValueL, CSSValueA, CSSValueB, CSSValueAlpha>(*lightness, originColorAsLab, [](auto lightness) {
    1272         return std::max(0.0, lightness);
    1273     });
    1274     auto resolvedAValue = resolveRelativeColorChannel<CSSValueL, CSSValueA, CSSValueB, CSSValueAlpha>(*aValue, originColorAsLab);
    1275     auto resolvedBValue = resolveRelativeColorChannel<CSSValueL, CSSValueA, CSSValueB, CSSValueAlpha>(*bValue, originColorAsLab);
    1276     auto resolvedAlpha = resolveRelativeColorChannel<CSSValueL, CSSValueA, CSSValueB, CSSValueAlpha>(*alpha, originColorAsLab);
    1277 
    1278     return Lab<float> { static_cast<float>(resolvedLightness), static_cast<float>(resolvedAValue), static_cast<float>(resolvedBValue), static_cast<float>(resolvedAlpha) };
     1412    auto normalizedLightness = std::max(0.0, *lightness);
     1413
     1414    return Lab<float> { static_cast<float>(normalizedLightness), static_cast<float>(*aValue), static_cast<float>(*bValue), static_cast<float>(*alpha) };
    12791415}
    12801416
     
    13241460        return { };
    13251461
    1326     auto lightness = consumePercentOrIdent<CSSValueL>(args);
     1462    auto originColorAsLCH = originColor.toColorTypeLossy<LCHA<float>>();
     1463
     1464    CSSCalcSymbolTable symbolTable {
     1465        { CSSValueL, CSSUnitType::CSS_PERCENTAGE, originColorAsLCH.lightness },
     1466        { CSSValueC, CSSUnitType::CSS_NUMBER, originColorAsLCH.chroma },
     1467        { CSSValueH, CSSUnitType::CSS_DEG, originColorAsLCH.hue },
     1468        { CSSValueAlpha, CSSUnitType::CSS_PERCENTAGE, originColorAsLCH.alpha * 100.0 }
     1469    };
     1470
     1471    auto lightness = consumePercentOrIdent(args, symbolTable);
    13271472    if (!lightness)
    13281473        return { };
    13291474
    1330     auto chroma = consumeNumberOrIdent<CSSValueC>(args);
     1475    auto chroma = consumeNumberOrIdent(args, symbolTable);
    13311476    if (!chroma)
    13321477        return { };
    13331478
    1334     auto hue = consumeHueOrIdent<CSSValueH>(args, context);
     1479    auto hue = consumeHueOrIdent(args, symbolTable);
    13351480    if (!hue)
    13361481        return { };
    13371482
    1338     auto alpha = consumeOptionalAlphaOrIdent<CSSValueAlpha>(args);
     1483    auto alpha = consumeOptionalAlphaOrIdent(args, symbolTable);
    13391484    if (!alpha)
    13401485        return { };
     
    13431488        return { };
    13441489
    1345     auto originColorAsLCH = originColor.toColorTypeLossy<LCHA<float>>();
    1346 
    1347     auto resolvedLightness = resolveRelativeColorChannel<CSSValueL, CSSValueC, CSSValueH, CSSValueAlpha>(*lightness, originColorAsLCH, [](auto lightness) {
    1348         return std::max(0.0, lightness);
    1349     });
    1350     auto resolvedChroma = resolveRelativeColorChannel<CSSValueL, CSSValueC, CSSValueH, CSSValueAlpha>(*chroma, originColorAsLCH, [](auto chroma) {
    1351         return std::max(0.0, chroma);
    1352     });
    1353     auto resolvedHue = resolveRelativeColorChannel<CSSValueL, CSSValueC, CSSValueH, CSSValueAlpha>(*hue, originColorAsLCH, [](auto hue) {
    1354         return normalizeHue(hue);
    1355     });
    1356     auto resolvedAlpha = resolveRelativeColorChannel<CSSValueL, CSSValueC, CSSValueH, CSSValueAlpha>(*alpha, originColorAsLCH);
    1357 
    1358     return LCHA<float> { static_cast<float>(resolvedLightness), static_cast<float>(resolvedChroma), static_cast<float>(resolvedHue), static_cast<float>(resolvedAlpha) };
     1490    auto normalizedLightness = std::max(0.0, *lightness);
     1491    auto normalizedChroma = std::max(0.0, *chroma);
     1492    auto normalizedHue = normalizeHue(*hue);
     1493
     1494    return LCHA<float> { static_cast<float>(normalizedLightness), static_cast<float>(normalizedChroma), static_cast<float>(normalizedHue), static_cast<float>(*alpha) };
    13591495}
    13601496
Note: See TracChangeset for help on using the changeset viewer.