Changeset 278304 in webkit


Ignore:
Timestamp:
Jun 1, 2021 6:50:16 AM (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

    r278297 r278304  
     12021-06-01  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-31  Diego Pino Garcia  <dpino@igalia.com>
    215
  • trunk/LayoutTests/fast/css/parsing-relative-color-syntax-expected.txt

    r278290 r278304  
    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

    r278290 r278304  
    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

    r278303 r278304  
     12021-06-01  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-06-01  Alan Bujtas  <zalan@apple.com>
    284
  • trunk/Source/WebCore/Sources.txt

    r278290 r278304  
    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

    r278292 r278304  
    40804080                BC6D44ED0C07F2ED0072D2C9 /* JSHTMLEmbedElement.h in Headers */ = {isa = PBXBuildFile; fileRef = BC6D44EB0C07F2ED0072D2C9 /* JSHTMLEmbedElement.h */; };
    40814081                BC6D6E2609AF943500F59759 /* ScrollView.h in Headers */ = {isa = PBXBuildFile; fileRef = BC6D6E2509AF943500F59759 /* ScrollView.h */; settings = {ATTRIBUTES = (Private, ); }; };
     4082                BC709DE0266323CF00B9A21C /* CSSCalcSymbolTable.h in Headers */ = {isa = PBXBuildFile; fileRef = BC709DDE266323CE00B9A21C /* CSSCalcSymbolTable.h */; };
    40824083                BC772B3C0C4EA91E0083285F /* CSSHelper.h in Headers */ = {isa = PBXBuildFile; fileRef = BC772B360C4EA91E0083285F /* CSSHelper.h */; settings = {ATTRIBUTES = (Private, ); }; };
    40834084                BC772C470C4EB2C60083285F /* XMLHttpRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = BC772C450C4EB2C60083285F /* XMLHttpRequest.h */; };
     
    1430914310                BC6D6E2509AF943500F59759 /* ScrollView.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = ScrollView.h; sourceTree = "<group>"; };
    1431014311                BC6EB84526266B61003225A7 /* ColorLuminance.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ColorLuminance.cpp; sourceTree = "<group>"; };
     14312                BC709DDE266323CE00B9A21C /* CSSCalcSymbolTable.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CSSCalcSymbolTable.h; sourceTree = "<group>"; };
     14313                BC709DDF266323CE00B9A21C /* CSSCalcSymbolTable.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = CSSCalcSymbolTable.cpp; sourceTree = "<group>"; };
    1431114314                BC772B360C4EA91E0083285F /* CSSHelper.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = CSSHelper.h; sourceTree = "<group>"; };
    1431214315                BC772C440C4EB2C60083285F /* XMLHttpRequest.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = XMLHttpRequest.cpp; sourceTree = "<group>"; };
     
    2796327966                                BCF164542662A1220002F7EF /* CSSCalcPrimitiveValueNode.cpp */,
    2796427967                                BCF164532662A1220002F7EF /* CSSCalcPrimitiveValueNode.h */,
     27968                                BC709DDF266323CE00B9A21C /* CSSCalcSymbolTable.cpp */,
     27969                                BC709DDE266323CE00B9A21C /* CSSCalcSymbolTable.h */,
    2796527970                                49AE2D8C134EE50C0072920A /* CSSCalcValue.cpp */,
    2796627971                                49AE2D8D134EE50C0072920A /* CSSCalcValue.h */,
     
    3467734682                                BCAA487014A052530088FAC4 /* PlatformEventFactoryMac.h in Headers */,
    3467834683                                A723F77B1484CA4C008C6DBE /* PlatformExportMacros.h in Headers */,
     34684                                BC709DE0266323CF00B9A21C /* CSSCalcSymbolTable.h in Headers */,
    3467934685                                515BE1951D54F5FB00DD7C68 /* PlatformGamepad.h in Headers */,
    3468034686                                726D56E2253AE28D0002EF90 /* PlatformImage.h in Headers */,
  • trunk/Source/WebCore/css/calc/CSSCalcExpressionNodeParser.cpp

    r278290 r278304  
    3232#include "CSSCalcOperationNode.h"
    3333#include "CSSCalcPrimitiveValueNode.h"
     34#include "CSSCalcSymbolTable.h"
    3435#include "CSSCalcValue.h"
    3536#include "CSSParserToken.h"
     
    181182bool CSSCalcExpressionNodeParser::parseValue(CSSParserTokenRange& tokens, RefPtr<CSSCalcExpressionNode>& result)
    182183{
    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;
     184    auto makeCSSCalcPrimitiveValueNode = [&] (CSSUnitType type, double value) -> bool {
     185        if (calcUnitCategory(type) == CalculationCategory::Other)
     186            return false;
     187       
     188        result = CSSCalcPrimitiveValueNode::create(CSSPrimitiveValue::create(value, type));
     189        return true;
     190    };
     191
     192    auto token = tokens.consumeIncludingWhitespace();
     193
     194    switch (token.type()) {
     195    case IdentToken: {
     196        auto value = m_symbolTable.get(token.id());
     197        if (!value)
     198            return false;
     199        return makeCSSCalcPrimitiveValueNode(value->type, value->value);
     200    }
     201
     202    case NumberToken:
     203    case PercentageToken:
     204    case DimensionToken:
     205        return makeCSSCalcPrimitiveValueNode(token.unitType(), token.numericValue());
     206
     207    default:
     208        return false;
     209    }
     210
     211    ASSERT_NOT_REACHED();
     212    return false;
    196213}
    197214
  • trunk/Source/WebCore/css/calc/CSSCalcExpressionNodeParser.h

    r278290 r278304  
    3333
    3434class CSSCalcExpressionNode;
     35class CSSCalcSymbolTable;
    3536class CSSParserToken;
    3637class CSSParserTokenRange;
     
    3839class CSSCalcExpressionNodeParser {
    3940public:
    40     explicit CSSCalcExpressionNodeParser(CalculationCategory destinationCategory)
     41    explicit CSSCalcExpressionNodeParser(CalculationCategory destinationCategory, const CSSCalcSymbolTable& symbolTable)
    4142        : m_destinationCategory(destinationCategory)
     43        , m_symbolTable(symbolTable)
    4244    {
    4345    }
     
    5658
    5759    CalculationCategory m_destinationCategory;
     60    const CSSCalcSymbolTable& m_symbolTable;
    5861};
    5962
  • trunk/Source/WebCore/css/calc/CSSCalcSymbolTable.cpp

    r278303 r278304  
    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

    r278303 r278304  
    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

    r278291 r278304  
    304304}
    305305
    306 RefPtr<CSSCalcValue> CSSCalcValue::create(CSSValueID function, const CSSParserTokenRange& tokens, CalculationCategory destinationCategory, ValueRange range)
    307 {
    308     CSSCalcExpressionNodeParser parser(destinationCategory);
     306RefPtr<CSSCalcValue> CSSCalcValue::create(CSSValueID function, const CSSParserTokenRange& tokens, CalculationCategory destinationCategory, ValueRange range, const CSSCalcSymbolTable& symbolTable)
     307{
     308    CSSCalcExpressionNodeParser parser(destinationCategory, symbolTable);
    309309    auto expression = parser.parseCalc(tokens, function);
    310310    if (!expression)
     
    314314    return result;
    315315}
    316    
     316
     317RefPtr<CSSCalcValue> CSSCalcValue::create(CSSValueID function, const CSSParserTokenRange& tokens, CalculationCategory destinationCategory, ValueRange range)
     318{
     319    return create(function, tokens, destinationCategory, range, { });
     320}
     321
    317322RefPtr<CSSCalcValue> CSSCalcValue::create(const CalculationValue& value, const RenderStyle& style)
    318323{
  • trunk/Source/WebCore/css/calc/CSSCalcValue.h

    r278290 r278304  
    4040
    4141class CSSCalcExpressionNode;
     42class CSSCalcSymbolTable;
    4243class CSSParserTokenRange;
    4344class CSSToLengthConversionData;
     
    5051class CSSCalcValue final : public CSSValue {
    5152public:
     53    static RefPtr<CSSCalcValue> create(CSSValueID function, const CSSParserTokenRange&, CalculationCategory destinationCategory, ValueRange, const CSSCalcSymbolTable&);
    5254    static RefPtr<CSSCalcValue> create(CSSValueID function, const CSSParserTokenRange&, CalculationCategory destinationCategory, ValueRange);
    5355    static RefPtr<CSSCalcValue> create(const CalculationValue&, const RenderStyle&);
  • trunk/Source/WebCore/css/makevalues.pl

    r278290 r278304  
    165165#include <string.h>
    166166#include <wtf/Forward.h>
     167#include <wtf/HashFunctions.h>
     168#include <wtf/HashTraits.h>
    167169
    168170namespace WebCore {
     
    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

    r278290 r278304  
    3131#include "CSSPropertyParserHelpers.h"
    3232
     33#include "CSSCalcSymbolTable.h"
    3334#include "CSSCalcValue.h"
    3435#include "CSSCanvasValue.h"
     
    100101class CalcParser {
    101102public:
    102     explicit CalcParser(CSSParserTokenRange& range, CalculationCategory destinationCategory, ValueRange valueRange = ValueRange::All, CSSValuePool& cssValuePool = CSSValuePool::singleton())
     103    explicit CalcParser(CSSParserTokenRange& range, CalculationCategory destinationCategory, ValueRange valueRange = ValueRange::All, const CSSCalcSymbolTable& symbolTable = { }, CSSValuePool& cssValuePool = CSSValuePool::singleton())
    103104        : m_sourceRange(range)
    104105        , m_range(range)
     
    108109        auto functionId = token.functionId();
    109110        if (CSSCalcValue::isCalcFunction(functionId))
    110             m_calcValue = CSSCalcValue::create(functionId, consumeFunction(m_range), destinationCategory, valueRange);
     111            m_calcValue = CSSCalcValue::create(functionId, consumeFunction(m_range), destinationCategory, valueRange, symbolTable);
    111112    }
    112113
     
    116117    {
    117118        if (!m_calcValue)
     119            return nullptr;
     120        m_sourceRange = m_range;
     121        return m_valuePool.createValue(WTFMove(m_calcValue));
     122    }
     123
     124    RefPtr<CSSPrimitiveValue> consumeValueIfCategory(CalculationCategory category)
     125    {
     126        if (!m_calcValue || m_calcValue->category() != category)
    118127            return nullptr;
    119128        m_sourceRange = m_range;
     
    219228
    220229    CalcParser calcParser(range, CalculationCategory::Number);
    221     if (const CSSCalcValue* calculation = calcParser.value()) {
    222         if (calculation->category() != CalculationCategory::Number)
    223             return std::nullopt;
     230    if (auto calculation = calcParser.value(); calculation && calculation->category() == CalculationCategory::Number)
    224231        return calcParser.consumeIntegerTypeRaw<IntType>(minimumValue);
    225     }
    226232
    227233    return std::nullopt;
     
    274280    if (token.type() == FunctionToken) {
    275281        CalcParser calcParser(range, CalculationCategory::Number, valueRange);
    276         if (const auto* calcValue = calcParser.value()) {
    277             if (calcValue->category() == CalculationCategory::Number)
    278                 return calcParser.consumeValue();
    279         }
    280         return nullptr;
     282        return calcParser.consumeValueIfCategory(CalculationCategory::Number);
    281283    }
    282284
     
    400402    if (token.type() == FunctionToken) {
    401403        CalcParser calcParser(range, CalculationCategory::Length, valueRange);
    402         if (calcParser.value() && calcParser.value()->category() == CalculationCategory::Length)
    403             return calcParser.consumeValue();
     404        return calcParser.consumeValueIfCategory(CalculationCategory::Length);
    404405    }
    405406
     
    435436    const CSSParserToken& token = range.peek();
    436437    if (token.type() == FunctionToken) {
    437         CalcParser calcParser(range, CalculationCategory::Percent, valueRange, cssValuePool);
    438         if (const CSSCalcValue* calculation = calcParser.value()) {
    439             if (calculation->category() == CalculationCategory::Percent)
    440                 return calcParser.consumeValue();
    441         }
    442         return nullptr;
     438        CalcParser calcParser(range, CalculationCategory::Percent, valueRange, { }, cssValuePool);
     439        return calcParser.consumeValueIfCategory(CalculationCategory::Percent);
    443440    }
    444441
     
    481478
    482479    CalcParser calcParser(range, CalculationCategory::Length, valueRange);
    483     if (const CSSCalcValue* calculation = calcParser.value()) {
    484         if (canConsumeCalcValue(calculation->category(), cssParserMode))
    485             return calcParser.consumeLengthOrPercentRaw();
    486     }
     480    if (auto calculation = calcParser.value(); calculation && canConsumeCalcValue(calculation->category(), cssParserMode))
     481        return calcParser.consumeLengthOrPercentRaw();
    487482    return std::nullopt;
    488483}
     
    493488    if (token.type() == FunctionToken) {
    494489        CalcParser calcParser(range, CalculationCategory::Length, valueRange);
    495         if (const CSSCalcValue* calculation = calcParser.value()) {
    496             if (canConsumeCalcValue(calculation->category(), cssParserMode))
    497                 return calcParser.consumeValue();
    498         }
     490        if (auto calculation = calcParser.value(); calculation && canConsumeCalcValue(calculation->category(), cssParserMode))
     491            return calcParser.consumeValue();
    499492        return nullptr;
    500493    }
     
    545538    const CSSParserToken& token = range.peek();
    546539    if (token.type() == FunctionToken) {
    547         CalcParser calcParser(range, CalculationCategory::Angle, ValueRange::All, cssValuePool);
    548         if (const CSSCalcValue* calculation = calcParser.value()) {
    549             if (calculation->category() == CalculationCategory::Angle)
    550                 return calcParser.consumeValue();
    551         }
    552         return nullptr;
     540        CalcParser calcParser(range, CalculationCategory::Angle, ValueRange::All, { }, cssValuePool);
     541        return calcParser.consumeValueIfCategory(CalculationCategory::Angle);
    553542    }
    554543
     
    562551{
    563552    const CSSParserToken& token = range.peek();
    564     if (token.type() == DimensionToken) {
     553    switch (token.type()) {
     554    case DimensionToken:
    565555        switch (token.unitType()) {
    566556        case CSSUnitType::CSS_DEG:
     
    570560            return CSSValuePool::singleton().createValue(range.consumeIncludingWhitespace().numericValue(), token.unitType());
    571561        default:
    572             return nullptr;
    573         }
    574     }
    575     if (token.type() == NumberToken && shouldAcceptUnitlessValue(token.numericValue(), cssParserMode, unitless, unitlessZero))
    576         return CSSValuePool::singleton().createValue(range.consumeIncludingWhitespace().numericValue(), CSSUnitType::CSS_DEG);
    577 
    578     if (token.type() == PercentageToken)
     562            break;
     563        }
     564        break;
     565   
     566    case NumberToken:
     567        if (shouldAcceptUnitlessValue(token.numericValue(), cssParserMode, unitless, unitlessZero))
     568            return CSSValuePool::singleton().createValue(range.consumeIncludingWhitespace().numericValue(), CSSUnitType::CSS_DEG);
     569        break;
     570
     571    case PercentageToken:
    579572        return consumePercent(range, valueRange);
    580573
    581      if (token.type() != FunctionToken)
    582          return nullptr;
    583 
    584     CalcParser angleCalcParser(range, CalculationCategory::Angle, valueRange);
    585     if (const CSSCalcValue* calculation = angleCalcParser.value()) {
    586         if (calculation->category() == CalculationCategory::Angle)
    587             return angleCalcParser.consumeValue();
    588     }
    589 
    590     CalcParser percentCalcParser(range, CalculationCategory::Percent, valueRange);
    591     if (const CSSCalcValue* calculation = percentCalcParser.value()) {
    592         if (calculation->category() == CalculationCategory::Percent)
    593             return percentCalcParser.consumeValue();
    594     }
     574    case FunctionToken: {
     575        CalcParser angleCalcParser(range, CalculationCategory::Angle, valueRange);
     576        if (const CSSCalcValue* calculation = angleCalcParser.value()) {
     577            if (calculation->category() == CalculationCategory::Angle)
     578                return angleCalcParser.consumeValue();
     579        }
     580
     581        CalcParser percentCalcParser(range, CalculationCategory::Percent, valueRange);
     582        if (const CSSCalcValue* calculation = percentCalcParser.value()) {
     583            if (calculation->category() == CalculationCategory::Percent)
     584                return percentCalcParser.consumeValue();
     585        }
     586   
     587        break;
     588    }
     589   
     590    default:
     591        break;
     592    }
     593
    595594    return nullptr;
    596595}
     
    615614
    616615    CalcParser calcParser(range, CalculationCategory::Time, valueRange);
    617     if (const CSSCalcValue* calculation = calcParser.value()) {
    618         if (calculation->category() == CalculationCategory::Time)
    619             return calcParser.consumeValue();
    620     }
    621     return nullptr;
     616    return calcParser.consumeValueIfCategory(CalculationCategory::Time);
    622617}
    623618
     
    745740
    746741    if (auto alphaParameter = consumeNumberOrPercentDividedBy100Raw(range))
    747         return clampTo(*alphaParameter, 0.0, 1.0);
     742        return std::clamp(*alphaParameter, 0.0, 1.0);
    748743
    749744    return std::nullopt;
    750745}
    751746
    752 template<CSSValueID... allowedIdents> static std::optional<Variant<double, CSSValueID>> consumeOptionalAlphaOrIdent(CSSParserTokenRange& range)
    753 {
    754     if (auto alpha = consumeOptionalAlpha(range))
    755         return { *alpha };
    756 
    757     if (auto ident = consumeIdentRaw<allowedIdents...>(range))
    758         return { *ident };
    759 
     747static std::optional<double> consumeOptionalAlphaOrIdent(CSSParserTokenRange& range, const CSSCalcSymbolTable& symbolTable)
     748{
     749    if (!consumeSlashIncludingWhitespace(range))
     750        return 1.0;
     751
     752    auto normalizePercent = [](double percent) {
     753        return std::clamp(percent / 100.0, 0.0, 1.0);
     754    };
     755
     756    auto normalizeNumber = [](double alpha) {
     757        return std::clamp(alpha, 0.0, 1.0);
     758    };
     759
     760    const CSSParserToken& token = range.peek();
     761
     762    switch (token.type()) {
     763    case FunctionToken: {
     764        CalcParser percentageCalcParser(range, CalculationCategory::Percent, ValueRange::All, symbolTable);
     765        if (auto percent = percentageCalcParser.consumePercentRaw())
     766            return normalizePercent(*percent);
     767       
     768        CalcParser numberCalcParser(range, CalculationCategory::Number, ValueRange::All, symbolTable);
     769        if (auto number = numberCalcParser.consumeNumberRaw())
     770            return normalizeNumber(*number);
     771
     772        return std::nullopt;
     773    }
     774
     775    case PercentageToken:
     776        if (std::isinf(token.numericValue()))
     777            return std::nullopt;
     778        return normalizePercent(range.consumeIncludingWhitespace().numericValue());
     779
     780    case NumberToken:
     781        return normalizeNumber(range.consumeIncludingWhitespace().numericValue());
     782
     783    case IdentToken:
     784        if (auto variable = symbolTable.get(range.consumeIncludingWhitespace().id())) {
     785            switch (variable->type) {
     786            case CSSUnitType::CSS_PERCENTAGE:
     787                return normalizePercent(variable->value);
     788
     789            case CSSUnitType::CSS_NUMBER:
     790                return normalizeNumber(variable->value);
     791
     792            default:
     793                return std::nullopt;
     794            }
     795        }
     796        return std::nullopt;
     797
     798    default:
     799        return std::nullopt;
     800    }
     801
     802    ASSERT_NOT_REACHED();
    760803    return std::nullopt;
    761804}
     
    769812}
    770813
    771 template<CSSValueID... allowedIdents> static std::optional<Variant<double, CSSValueID>> consumeHueOrIdent(CSSParserTokenRange& range, const CSSParserContext& context)
    772 {
    773     if (auto hue = consumeHue(range, context))
    774         return { *hue };
    775 
    776     if (auto ident = consumeIdentRaw<allowedIdents...>(range))
    777         return { *ident };
    778 
     814static std::optional<double> consumeHueOrIdent(CSSParserTokenRange& range, const CSSCalcSymbolTable& symbolTable)
     815{
     816    const CSSParserToken& token = range.peek();
     817
     818    switch (token.type()) {
     819    case FunctionToken: {
     820        CalcParser angleCalcParser(range, CalculationCategory::Angle, ValueRange::All, symbolTable);
     821        if (auto angle = angleCalcParser.consumeAngleRaw())
     822            return CSSPrimitiveValue::computeDegrees(angle->type, angle->value);
     823       
     824        CalcParser numberCalcParser(range, CalculationCategory::Number, ValueRange::All, symbolTable);
     825        return numberCalcParser.consumeNumberRaw();
     826    }
     827   
     828    case DimensionToken: {
     829        auto unitType = token.unitType();
     830        switch (unitType) {
     831        case CSSUnitType::CSS_DEG:
     832        case CSSUnitType::CSS_RAD:
     833        case CSSUnitType::CSS_GRAD:
     834        case CSSUnitType::CSS_TURN:
     835            return CSSPrimitiveValue::computeDegrees(unitType, range.consumeIncludingWhitespace().numericValue());
     836        default:
     837            return std::nullopt;
     838        }
     839    }
     840   
     841    case NumberToken:
     842        return range.consumeIncludingWhitespace().numericValue();
     843
     844    case IdentToken:
     845        if (auto variable = symbolTable.get(range.consumeIncludingWhitespace().id())) {
     846            switch (variable->type) {
     847            case CSSUnitType::CSS_DEG:
     848            case CSSUnitType::CSS_RAD:
     849            case CSSUnitType::CSS_GRAD:
     850            case CSSUnitType::CSS_TURN:
     851                return CSSPrimitiveValue::computeDegrees(variable->type, variable->value);
     852
     853            case CSSUnitType::CSS_NUMBER:
     854                return variable->value;
     855
     856            default:
     857                return std::nullopt;
     858            }
     859        }
     860        return std::nullopt;
     861
     862    default:
     863        return std::nullopt;
     864    }
     865
     866    ASSERT_NOT_REACHED();
    779867    return std::nullopt;
    780868}
     
    785873}
    786874
    787 template<CSSValueID... allowedIdents> static std::optional<Variant<double, CSSValueID>> consumeNumberOrIdent(CSSParserTokenRange& range)
    788 {
    789     if (auto number = consumeNumberRaw(range))
    790         return { *number };
    791 
    792     if (auto ident = consumeIdentRaw<allowedIdents...>(range))
    793         return { *ident };
    794 
     875static std::optional<double> consumeNumberOrIdent(CSSParserTokenRange& range, const CSSCalcSymbolTable& symbolTable)
     876{
     877    const CSSParserToken& token = range.peek();
     878
     879    switch (token.type()) {
     880    case FunctionToken: {
     881        CalcParser calcParser(range, CalculationCategory::Number, ValueRange::All, symbolTable);
     882        return calcParser.consumeNumberRaw();
     883    }
     884
     885    case NumberToken:
     886        return range.consumeIncludingWhitespace().numericValue();
     887
     888    case IdentToken:
     889        if (auto variable = symbolTable.get(range.consumeIncludingWhitespace().id())) {
     890            switch (variable->type) {
     891            case CSSUnitType::CSS_NUMBER:
     892                return variable->value;
     893
     894            default:
     895                return std::nullopt;
     896            }
     897        }
     898        return std::nullopt;
     899
     900    default:
     901        return std::nullopt;
     902    }
     903
     904    ASSERT_NOT_REACHED();
    795905    return std::nullopt;
    796906}
    797907
    798 template<CSSValueID... allowedIdents> static std::optional<Variant<double, CSSValueID>> consumePercentOrIdent(CSSParserTokenRange& range)
    799 {
    800     if (auto percent = consumePercentRaw(range))
    801         return { *percent };
    802 
    803     if (auto ident = consumeIdentRaw<allowedIdents...>(range))
    804         return { *ident };
    805 
     908static std::optional<double> consumePercentOrIdent(CSSParserTokenRange& range, const CSSCalcSymbolTable& symbolTable)
     909{
     910    const CSSParserToken& token = range.peek();
     911
     912    switch (token.type()) {
     913    case FunctionToken: {
     914        CalcParser calcParser(range, CalculationCategory::Percent, ValueRange::All, symbolTable);
     915        return calcParser.consumePercentRaw();
     916    }
     917
     918    case PercentageToken:
     919        if (std::isinf(token.numericValue()))
     920            return std::nullopt;
     921        return range.consumeIncludingWhitespace().numericValue();
     922
     923    case IdentToken:
     924        if (auto variable = symbolTable.get(range.consumeIncludingWhitespace().id())) {
     925            switch (variable->type) {
     926            case CSSUnitType::CSS_PERCENTAGE:
     927                return variable->value;
     928
     929            default:
     930                return std::nullopt;
     931            }
     932        }
     933        return std::nullopt;
     934
     935    default:
     936        return std::nullopt;
     937    }
     938
     939    ASSERT_NOT_REACHED();
    806940    return std::nullopt;
    807941}
    808942
    809 template<CSSValueID C1, CSSValueID C2, CSSValueID C3, CSSValueID AlphaChannel, typename ColorType> static auto extractChannelValue(CSSValueID channel, const ColorType& originColor) -> typename ColorType::ComponentType
    810 {
    811     auto components = asColorComponents(originColor);
    812     switch (channel) {
    813     case C1:
    814         return components[0];
    815     case C2:
    816         return components[1];
    817     case C3:
    818         return components[2];
    819     case AlphaChannel:
    820         return components[3];
     943enum class RGBComponentType { Number, Percentage };
     944
     945static std::optional<uint8_t> consumeNumberOrPercentOrIdentNormalizedForRelativeRGB(CSSParserTokenRange& range, const CSSCalcSymbolTable& symbolTable)
     946{
     947    auto normalizePercent = [](double percent) {
     948        return convertPrescaledSRGBAFloatToSRGBAByte(percent / 100.0 * 255.0);
     949    };
     950
     951    auto normalizeNumber = [](double number) {
     952        return convertPrescaledSRGBAFloatToSRGBAByte(number);
     953    };
     954
     955    const CSSParserToken& token = range.peek();
     956
     957    switch (token.type()) {
     958    case FunctionToken: {
     959        CalcParser percentageCalcParser(range, CalculationCategory::Percent, ValueRange::All, symbolTable);
     960        if (auto percent = percentageCalcParser.consumePercentRaw())
     961            return normalizePercent(*percent);
     962       
     963        CalcParser numberCalcParser(range, CalculationCategory::Number, ValueRange::All, symbolTable);
     964        if (auto number = numberCalcParser.consumeNumberRaw())
     965            return normalizeNumber(*number);
     966       
     967        return std::nullopt;
     968    }
     969
     970    case PercentageToken:
     971        if (std::isinf(token.numericValue()))
     972            return std::nullopt;
     973        return normalizePercent(range.consumeIncludingWhitespace().numericValue());
     974
     975    case NumberToken:
     976        return normalizeNumber(range.consumeIncludingWhitespace().numericValue());
     977
     978    case IdentToken:
     979        if (auto variable = symbolTable.get(range.consumeIncludingWhitespace().id())) {
     980            switch (variable->type) {
     981            case CSSUnitType::CSS_PERCENTAGE:
     982                return normalizePercent(variable->value);
     983
     984            case CSSUnitType::CSS_NUMBER:
     985                return normalizeNumber(variable->value);
     986
     987            default:
     988                return std::nullopt;
     989            }
     990        }
     991        return std::nullopt;
     992
    821993    default:
    822         ASSERT_NOT_REACHED();
    823     }
    824 
    825     return 0;
    826 }
    827 
    828 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)
    829 {
    830     return switchOn(parsedChannel,
    831         [&] (double value) {
    832             return valueTransformer(value);
    833         },
    834         [&] (CSSValueID channel) {
    835             return extractChannelValue<C1, C2, C3, AlphaChannel>(channel, originColor);
    836         }
    837     );
    838 }
    839 
    840 template<CSSValueID C1, CSSValueID C2, CSSValueID C3, CSSValueID AlphaChannel, typename ColorType> static decltype(auto) resolveRelativeColorChannel(const Variant<double, CSSValueID>& parsedChannel, const ColorType& originColor)
    841 {
    842     return resolveRelativeColorChannel<C1, C2, C3, AlphaChannel>(parsedChannel, originColor, [](auto value) { return value; });
    843 }
    844 
    845 enum class RGBComponentType { Number, Percentage };
     994        return std::nullopt;
     995    }
     996
     997    ASSERT_NOT_REACHED();
     998    return std::nullopt;
     999}
    8461000
    8471001static uint8_t clampRGBComponent(double value, RGBComponentType componentType)
     
    9231077        return { };
    9241078
    925     std::optional<RGBComponentType> componentType;
    926     auto redResult = consumeRelativeRGBComponent<CSSValueR>(args, componentType);
    927     if (!redResult)
    928         return { };
    929     auto red = redResult->value;
    930     componentType = redResult->type;
    931 
    932     auto greenResult = consumeRelativeRGBComponent<CSSValueG>(args, componentType);
    933     if (!greenResult)
    934         return { };
    935     auto green = greenResult->value;
    936     componentType = greenResult->type;
    937 
    938     auto blueResult = consumeRelativeRGBComponent<CSSValueB>(args, componentType);
    939     if (!blueResult)
    940         return { };
    941     auto blue = blueResult->value;
    942     componentType = blueResult->type;
    943 
    944     auto alpha = consumeOptionalAlphaOrIdent<CSSValueAlpha>(args);
     1079    auto originColorAsSRGB = originColor.toColorTypeLossy<SRGBA<float>>();
     1080
     1081    CSSCalcSymbolTable symbolTable {
     1082        { CSSValueR, CSSUnitType::CSS_PERCENTAGE, originColorAsSRGB.red * 100.0 },
     1083        { CSSValueG, CSSUnitType::CSS_PERCENTAGE, originColorAsSRGB.green * 100.0 },
     1084        { CSSValueB, CSSUnitType::CSS_PERCENTAGE, originColorAsSRGB.blue * 100.0 },
     1085        { CSSValueAlpha, CSSUnitType::CSS_PERCENTAGE, originColorAsSRGB.alpha * 100.0 }
     1086    };
     1087
     1088    auto red = consumeNumberOrPercentOrIdentNormalizedForRelativeRGB(args, symbolTable);
     1089    if (!red)
     1090        return { };
     1091
     1092    auto green = consumeNumberOrPercentOrIdentNormalizedForRelativeRGB(args, symbolTable);
     1093    if (!green)
     1094        return { };
     1095
     1096    auto blue = consumeNumberOrPercentOrIdentNormalizedForRelativeRGB(args, symbolTable);
     1097    if (!blue)
     1098        return { };
     1099
     1100    auto alpha = consumeOptionalAlphaOrIdent(args, symbolTable);
    9451101    if (!alpha)
    9461102        return { };
     
    9491105        return { };
    9501106
    951     // After parsing, convert identifiers to values from the origin color.
    952 
    953     // FIXME: Do we want to being doing this in uint8_t values? Or should we use
    954     // higher precision and clamp at the end? It won't make a difference until we
    955     // support calculations on the origin's components.
    956     auto originColorAsSRGB = originColor.toSRGBALossy<uint8_t>();
    957 
    958     auto resolvedComponentType = componentType.value_or(RGBComponentType::Percentage);
    959     auto channelResolver = [resolvedComponentType](auto value) {
    960         return clampRGBComponent(value, resolvedComponentType);
    961     };
    962 
    963     auto resolvedRed = resolveRelativeColorChannel<CSSValueR, CSSValueG, CSSValueB, CSSValueAlpha>(red, originColorAsSRGB, channelResolver);
    964     auto resolvedGreen = resolveRelativeColorChannel<CSSValueR, CSSValueG, CSSValueB, CSSValueAlpha>(green, originColorAsSRGB, channelResolver);
    965     auto resolvedBlue = resolveRelativeColorChannel<CSSValueR, CSSValueG, CSSValueB, CSSValueAlpha>(blue, originColorAsSRGB, channelResolver);
    966     auto resolvedAlpha = resolveRelativeColorChannel<CSSValueR, CSSValueG, CSSValueB, CSSValueAlpha>(*alpha, originColorAsSRGB, [](auto value) {
    967         return convertFloatAlphaTo<uint8_t>(value);
    968     });
    969 
    970     return SRGBA<uint8_t> { resolvedRed, resolvedGreen, resolvedBlue, resolvedAlpha };
     1107    auto normalizedAlpha = convertFloatAlphaTo<uint8_t>(*alpha);
     1108
     1109    return SRGBA<uint8_t> { *red, *green, *blue, normalizedAlpha };
    9711110}
    9721111
     
    10501189        return { };
    10511190
    1052     auto hue = consumeHueOrIdent<CSSValueH>(args, context);
     1191    auto originColorAsHSL = originColor.toColorTypeLossy<HSLA<float>>();
     1192
     1193    CSSCalcSymbolTable symbolTable {
     1194        { CSSValueH, CSSUnitType::CSS_DEG, originColorAsHSL.hue },
     1195        { CSSValueS, CSSUnitType::CSS_PERCENTAGE, originColorAsHSL.saturation },
     1196        { CSSValueL, CSSUnitType::CSS_PERCENTAGE, originColorAsHSL.lightness },
     1197        { CSSValueAlpha, CSSUnitType::CSS_PERCENTAGE, originColorAsHSL.alpha * 100.0 }
     1198    };
     1199
     1200    auto hue = consumeHueOrIdent(args, symbolTable);
    10531201    if (!hue)
    10541202        return { };
    10551203
    1056     auto saturation = consumePercentOrIdent<CSSValueS>(args);
     1204    auto saturation = consumePercentOrIdent(args, symbolTable);
    10571205    if (!saturation)
    10581206        return { };
    10591207
    1060     auto lightness = consumePercentOrIdent<CSSValueL>(args);
     1208    auto lightness = consumePercentOrIdent(args, symbolTable);
    10611209    if (!lightness)
    10621210        return { };
    10631211
    1064     auto alpha = consumeOptionalAlphaOrIdent<CSSValueAlpha>(args);
     1212    auto alpha = consumeOptionalAlphaOrIdent(args, symbolTable);
    10651213    if (!alpha)
    10661214        return { };
     
    10691217        return { };
    10701218
    1071     // After parsing, convert identifiers to values from the origin color.
    1072 
    1073     auto originColorAsHSL = originColor.toColorTypeLossy<HSLA<float>>();
    1074 
    1075     auto resolvedHue = resolveRelativeColorChannel<CSSValueH, CSSValueS, CSSValueL, CSSValueAlpha>(*hue, originColorAsHSL, [](auto hue) {
    1076         return normalizeHue(hue);
    1077     });
    1078     auto resolvedSaturation = resolveRelativeColorChannel<CSSValueH, CSSValueS, CSSValueL, CSSValueAlpha>(*saturation, originColorAsHSL, [](auto saturation) {
    1079         return clampTo(saturation, 0.0, 100.0);
    1080     });
    1081     auto resolvedLightness = resolveRelativeColorChannel<CSSValueH, CSSValueS, CSSValueL, CSSValueAlpha>(*lightness, originColorAsHSL, [](auto lightness) {
    1082         return clampTo(lightness, 0.0, 100.0);
    1083     });
    1084     auto resolvedAlpha = resolveRelativeColorChannel<CSSValueH, CSSValueS, CSSValueL, CSSValueAlpha>(*alpha, originColorAsHSL);
    1085 
    1086     return convertColor<SRGBA<uint8_t>>(HSLA<float> { static_cast<float>(resolvedHue), static_cast<float>(resolvedSaturation), static_cast<float>(resolvedLightness), static_cast<float>(resolvedAlpha) });
     1219    auto normalizedHue = normalizeHue(*hue);
     1220    auto normalizedSaturation = std::clamp(*saturation, 0.0, 100.0);
     1221    auto normalizedLightness = std::clamp(*lightness, 0.0, 100.0);
     1222    auto normalizedAlpha = std::clamp(*alpha, 0.0, 1.0);
     1223
     1224    return convertColor<SRGBA<uint8_t>>(HSLA<float> { static_cast<float>(normalizedHue), static_cast<float>(normalizedSaturation), static_cast<float>(normalizedLightness), static_cast<float>(normalizedAlpha) });
    10871225}
    10881226
     
    11241262
    11251263    auto normalizedHue = normalizeHue(*hue);
    1126     auto normalizedSaturation = clampTo(*saturation, 0.0, 100.0);
    1127     auto normalizedLightness = clampTo(*lightness, 0.0, 100.0);
    1128     auto normalizedAlpha = clampTo(*alpha, 0.0, 1.0);
     1264    auto normalizedSaturation = std::clamp(*saturation, 0.0, 100.0);
     1265    auto normalizedLightness = std::clamp(*lightness, 0.0, 100.0);
     1266    auto normalizedAlpha = std::clamp(*alpha, 0.0, 1.0);
    11291267
    11301268    return convertColor<SRGBA<uint8_t>>(HSLA<float> { static_cast<float>(normalizedHue), static_cast<float>(normalizedSaturation), static_cast<float>(normalizedLightness), static_cast<float>(normalizedAlpha) });
    11311269}
    11321270
    1133 template<typename ComponentType>
    1134 struct WhitenessBlackness {
     1271template<typename ComponentType> struct WhitenessBlackness {
    11351272    ComponentType whiteness;
    11361273    ComponentType blackness;
     
    11661303        return { };
    11671304
    1168     auto hue = consumeHueOrIdent<CSSValueH>(args, context);
     1305    auto originColorAsHWB = originColor.toColorTypeLossy<HWBA<float>>();
     1306
     1307    CSSCalcSymbolTable symbolTable {
     1308        { CSSValueH, CSSUnitType::CSS_DEG, originColorAsHWB.hue },
     1309        { CSSValueW, CSSUnitType::CSS_PERCENTAGE, originColorAsHWB.whiteness },
     1310        { CSSValueB, CSSUnitType::CSS_PERCENTAGE, originColorAsHWB.blackness },
     1311        { CSSValueAlpha, CSSUnitType::CSS_PERCENTAGE, originColorAsHWB.alpha * 100.0 }
     1312    };
     1313
     1314    auto hue = consumeHueOrIdent(args, symbolTable);
    11691315    if (!hue)
    11701316        return { };
    11711317
    1172     auto whiteness = consumePercentOrIdent<CSSValueW>(args);
     1318    auto whiteness = consumePercentOrIdent(args, symbolTable);
    11731319    if (!whiteness)
    11741320        return { };
    11751321
    1176     auto blackness = consumePercentOrIdent<CSSValueB>(args);
     1322    auto blackness = consumePercentOrIdent(args, symbolTable);
    11771323    if (!blackness)
    11781324        return { };
    11791325
    1180     auto alpha = consumeOptionalAlphaOrIdent<CSSValueAlpha>(args);
     1326    auto alpha = consumeOptionalAlphaOrIdent(args, symbolTable);
    11811327    if (!alpha)
    11821328        return { };
     
    11851331        return { };
    11861332
    1187     // After parsing, convert identifiers to values from the origin color.
    1188 
    1189     auto originColorAsHWB = originColor.toColorTypeLossy<HWBA<float>>();
    1190 
    1191     auto resolvedHue = resolveRelativeColorChannel<CSSValueH, CSSValueW, CSSValueB, CSSValueAlpha>(*hue, originColorAsHWB, [](auto hue) {
    1192         return normalizeHue(hue);
    1193     });
    1194     auto resolvedWhiteness = resolveRelativeColorChannel<CSSValueH, CSSValueW, CSSValueB, CSSValueAlpha>(*whiteness, originColorAsHWB);
    1195     auto resolvedBlackness = resolveRelativeColorChannel<CSSValueH, CSSValueW, CSSValueB, CSSValueAlpha>(*blackness, originColorAsHWB);
    1196     auto resolvedAlpha = resolveRelativeColorChannel<CSSValueH, CSSValueW, CSSValueB, CSSValueAlpha>(*alpha, originColorAsHWB);
    1197 
    1198     auto [normalizedWhitness, normalizedBlackness] = normalizeWhitenessBlackness(resolvedWhiteness, resolvedBlackness);
    1199 
    1200     return convertColor<SRGBA<uint8_t>>(HWBA<float> { static_cast<float>(resolvedHue), static_cast<float>(normalizedWhitness), static_cast<float>(normalizedBlackness), static_cast<float>(resolvedAlpha) });
     1333    auto normalizedHue = normalizeHue(*hue);
     1334    auto [normalizedWhitness, normalizedBlackness] = normalizeWhitenessBlackness(*whiteness, *blackness);
     1335
     1336    return convertColor<SRGBA<uint8_t>>(HWBA<float> { static_cast<float>(normalizedHue), static_cast<float>(normalizedWhitness), static_cast<float>(normalizedBlackness), static_cast<float>(*alpha) });
    12011337}
    12021338
     
    12471383        return { };
    12481384
    1249     auto lightness = consumePercentOrIdent<CSSValueL>(args);
     1385    auto originColorAsLab = originColor.toColorTypeLossy<Lab<float>>();
     1386
     1387    CSSCalcSymbolTable symbolTable {
     1388        { CSSValueL, CSSUnitType::CSS_PERCENTAGE, originColorAsLab.lightness },
     1389        { CSSValueA, CSSUnitType::CSS_NUMBER, originColorAsLab.a },
     1390        { CSSValueB, CSSUnitType::CSS_NUMBER, originColorAsLab.b },
     1391        { CSSValueAlpha, CSSUnitType::CSS_PERCENTAGE, originColorAsLab.alpha * 100.0 }
     1392    };
     1393
     1394    auto lightness = consumePercentOrIdent(args, symbolTable);
    12501395    if (!lightness)
    12511396        return { };
    12521397
    1253     auto aValue = consumeNumberOrIdent<CSSValueA>(args);
     1398    auto aValue = consumeNumberOrIdent(args, symbolTable);
    12541399    if (!aValue)
    12551400        return { };
    12561401
    1257     auto bValue = consumeNumberOrIdent<CSSValueB>(args);
     1402    auto bValue = consumeNumberOrIdent(args, symbolTable);
    12581403    if (!bValue)
    12591404        return { };
    12601405
    1261     auto alpha = consumeOptionalAlphaOrIdent<CSSValueAlpha>(args);
     1406    auto alpha = consumeOptionalAlphaOrIdent(args, symbolTable);
    12621407    if (!alpha)
    12631408        return { };
     
    12661411        return { };
    12671412
    1268     // After parsing, convert identifiers to values from the origin color.
    1269 
    1270     auto originColorAsLab = originColor.toColorTypeLossy<Lab<float>>();
    1271 
    1272     auto resolvedLightness = resolveRelativeColorChannel<CSSValueL, CSSValueA, CSSValueB, CSSValueAlpha>(*lightness, originColorAsLab, [](auto lightness) {
    1273         return std::max(0.0, lightness);
    1274     });
    1275     auto resolvedAValue = resolveRelativeColorChannel<CSSValueL, CSSValueA, CSSValueB, CSSValueAlpha>(*aValue, originColorAsLab);
    1276     auto resolvedBValue = resolveRelativeColorChannel<CSSValueL, CSSValueA, CSSValueB, CSSValueAlpha>(*bValue, originColorAsLab);
    1277     auto resolvedAlpha = resolveRelativeColorChannel<CSSValueL, CSSValueA, CSSValueB, CSSValueAlpha>(*alpha, originColorAsLab);
    1278 
    1279     return Lab<float> { static_cast<float>(resolvedLightness), static_cast<float>(resolvedAValue), static_cast<float>(resolvedBValue), static_cast<float>(resolvedAlpha) };
     1413    auto normalizedLightness = std::max(0.0, *lightness);
     1414
     1415    return Lab<float> { static_cast<float>(normalizedLightness), static_cast<float>(*aValue), static_cast<float>(*bValue), static_cast<float>(*alpha) };
    12801416}
    12811417
     
    13251461        return { };
    13261462
    1327     auto lightness = consumePercentOrIdent<CSSValueL>(args);
     1463    auto originColorAsLCH = originColor.toColorTypeLossy<LCHA<float>>();
     1464
     1465    CSSCalcSymbolTable symbolTable {
     1466        { CSSValueL, CSSUnitType::CSS_PERCENTAGE, originColorAsLCH.lightness },
     1467        { CSSValueC, CSSUnitType::CSS_NUMBER, originColorAsLCH.chroma },
     1468        { CSSValueH, CSSUnitType::CSS_DEG, originColorAsLCH.hue },
     1469        { CSSValueAlpha, CSSUnitType::CSS_PERCENTAGE, originColorAsLCH.alpha * 100.0 }
     1470    };
     1471
     1472    auto lightness = consumePercentOrIdent(args, symbolTable);
    13281473    if (!lightness)
    13291474        return { };
    13301475
    1331     auto chroma = consumeNumberOrIdent<CSSValueC>(args);
     1476    auto chroma = consumeNumberOrIdent(args, symbolTable);
    13321477    if (!chroma)
    13331478        return { };
    13341479
    1335     auto hue = consumeHueOrIdent<CSSValueH>(args, context);
     1480    auto hue = consumeHueOrIdent(args, symbolTable);
    13361481    if (!hue)
    13371482        return { };
    13381483
    1339     auto alpha = consumeOptionalAlphaOrIdent<CSSValueAlpha>(args);
     1484    auto alpha = consumeOptionalAlphaOrIdent(args, symbolTable);
    13401485    if (!alpha)
    13411486        return { };
     
    13441489        return { };
    13451490
    1346     auto originColorAsLCH = originColor.toColorTypeLossy<LCHA<float>>();
    1347 
    1348     auto resolvedLightness = resolveRelativeColorChannel<CSSValueL, CSSValueC, CSSValueH, CSSValueAlpha>(*lightness, originColorAsLCH, [](auto lightness) {
    1349         return std::max(0.0, lightness);
    1350     });
    1351     auto resolvedChroma = resolveRelativeColorChannel<CSSValueL, CSSValueC, CSSValueH, CSSValueAlpha>(*chroma, originColorAsLCH, [](auto chroma) {
    1352         return std::max(0.0, chroma);
    1353     });
    1354     auto resolvedHue = resolveRelativeColorChannel<CSSValueL, CSSValueC, CSSValueH, CSSValueAlpha>(*hue, originColorAsLCH, [](auto hue) {
    1355         return normalizeHue(hue);
    1356     });
    1357     auto resolvedAlpha = resolveRelativeColorChannel<CSSValueL, CSSValueC, CSSValueH, CSSValueAlpha>(*alpha, originColorAsLCH);
    1358 
    1359     return LCHA<float> { static_cast<float>(resolvedLightness), static_cast<float>(resolvedChroma), static_cast<float>(resolvedHue), static_cast<float>(resolvedAlpha) };
     1491    auto normalizedLightness = std::max(0.0, *lightness);
     1492    auto normalizedChroma = std::max(0.0, *chroma);
     1493    auto normalizedHue = normalizeHue(*hue);
     1494
     1495    return LCHA<float> { static_cast<float>(normalizedLightness), static_cast<float>(normalizedChroma), static_cast<float>(normalizedHue), static_cast<float>(*alpha) };
    13601496}
    13611497
Note: See TracChangeset for help on using the changeset viewer.