Title: [286196] trunk
Revision
286196
Author
wei...@apple.com
Date
2021-11-28 10:55:05 -0800 (Sun, 28 Nov 2021)

Log Message

[CSS Color 5] Update color-mix() to latest spec (again)
https://bugs.webkit.org/show_bug.cgi?id=233527

Reviewed by Dean Jackson.

Source/WebCore:

Updates color-mix() implementation with support for hue interpolation methods and
addresses various other spec changes (detailed below).

* Sources.txt:
* WebCore.xcodeproj/project.pbxproj:
Add ColorInterpolation.h/cpp

* css/CSSValueKeywords.in:
Add keywords need for the <color-space-interpolation> production. Separated out
from color-mix as these will be used for other CSS properties as well.

* css/parser/CSSPropertyParserHelpers.cpp:
(WebCore::CSSPropertyParserHelpers::consumeHueInterpolationMethod):
(WebCore::CSSPropertyParserHelpers::consumeColorInterpolationMethod):
(WebCore::CSSPropertyParserHelpers::consumeColorMixComponent):
(WebCore::CSSPropertyParserHelpers::normalizedMixPercentages):
(WebCore::CSSPropertyParserHelpers::makeColorTypeByNormalizingComponentsAfterMix<HWBA<float>>):
(WebCore::CSSPropertyParserHelpers::makeColorTypeByNormalizingComponentsAfterMix<HSLA<float>>):
(WebCore::CSSPropertyParserHelpers::makeColorTypeByNormalizingComponentsAfterMix<LCHA<float>>):
(WebCore::CSSPropertyParserHelpers::makeColorTypeByNormalizingComponentsAfterMix<OKLCHA<float>>):
(WebCore::CSSPropertyParserHelpers::mixColorComponentsUsingColorInterpolationMethod):
(WebCore::CSSPropertyParserHelpers::mixColorComponents):
(WebCore::CSSPropertyParserHelpers::parseColorMixFunctionParameters):
(WebCore::CSSPropertyParserHelpers::consumeColorMixColorSpaceAndComma): Deleted.
(WebCore::CSSPropertyParserHelpers::fixupHueComponentsPriorToMix): Deleted.
(WebCore::CSSPropertyParserHelpers::mixColorComponentsInColorSpace): Deleted.
Rework color-mix to support the hue interpolation method specification for polor color spaces
like lch, oklch, hsl and hwb. Also update implementation to match the current spec by restricting
mix percentages to between 0 and 100 and applying alpha multiplier when the mix percentages add
up to less than 100.

* platform/graphics/ColorInterpolation.cpp: Added.
* platform/graphics/ColorInterpolation.h: Added.
Add structures to represent color interpolation methods including optional hue
interpolation parameters for color spaces that require it and provides prenormalization
that fixes up hue angles depending on the method selected. Over time, more aspects of
interpolation should be moved here as we figure out what can be shared.

LayoutTests:

* fast/css/parsing-color-mix-expected.txt:
* fast/css/parsing-color-mix.html:
Update tests to include testing of hue interpolation methods, new
restrictions on percentage ranges and the new alpha multiplier.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (286195 => 286196)


--- trunk/LayoutTests/ChangeLog	2021-11-28 18:32:59 UTC (rev 286195)
+++ trunk/LayoutTests/ChangeLog	2021-11-28 18:55:05 UTC (rev 286196)
@@ -1,3 +1,15 @@
+2021-11-28  Sam Weinig  <wei...@apple.com>
+
+        [CSS Color 5] Update color-mix() to latest spec (again)
+        https://bugs.webkit.org/show_bug.cgi?id=233527
+
+        Reviewed by Dean Jackson.
+
+        * fast/css/parsing-color-mix-expected.txt:
+        * fast/css/parsing-color-mix.html:
+        Update tests to include testing of hue interpolation methods, new
+        restrictions on percentage ranges and the new alpha multiplier.
+
 2021-11-28  Philippe Normand  <pnorm...@igalia.com>
 
         Weird JS usage in webrtc/multi-video.html

Modified: trunk/LayoutTests/fast/css/parsing-color-mix-expected.txt (286195 => 286196)


--- trunk/LayoutTests/fast/css/parsing-color-mix-expected.txt	2021-11-28 18:32:59 UTC (rev 286195)
+++ trunk/LayoutTests/fast/css/parsing-color-mix-expected.txt	2021-11-28 18:55:05 UTC (rev 286196)
@@ -10,10 +10,55 @@
 PASS computedStyle("background-color", "color-mix(in hsl, hsl(120deg 10% 20%), 25% hsl(30deg 30% 40%))") is "rgb(61, 73, 54)"
 PASS computedStyle("background-color", "color-mix(in hsl, hsl(120deg 10% 20%), hsl(30deg 30% 40%) 25%)") is "rgb(61, 73, 54)"
 PASS computedStyle("background-color", "color-mix(in hsl, hsl(120deg 10% 20%) 25%, hsl(30deg 30% 40%) 75%)") is "rgb(112, 106, 67)"
-PASS computedStyle("background-color", "color-mix(in hsl, hsl(120deg 10% 20%) 50%, hsl(30deg 30% 40%) 150%)") is "rgb(112, 106, 67)"
-PASS computedStyle("background-color", "color-mix(in hsl, hsl(120deg 10% 20%) 12.5%, hsl(30deg 30% 40%) 37.5%)") is "rgb(112, 106, 67)"
+PASS computedStyle("background-color", "color-mix(in hsl, hsl(120deg 10% 20%) 30%, hsl(30deg 30% 40%) 90%)") is "rgb(112, 106, 67)"
+PASS computedStyle("background-color", "color-mix(in hsl, hsl(120deg 10% 20%) 12.5%, hsl(30deg 30% 40%) 37.5%)") is "rgba(112, 106, 67, 0.5)"
 PASS computedStyle("background-color", "color-mix(in hsl, hsl(120deg 10% 20%) 0%, hsl(30deg 30% 40%))") is "rgb(133, 102, 71)"
-PASS computedStyle("background-color", "color-mix(in hsl, hsl(120deg 10% 20%) -10%, hsl(30deg 30% 40%))") is "rgb(142, 97, 72)"
+-> no hue method specified, defaults to shorter
+PASS computedStyle("background-color", "color-mix(in hsl, hsl(40deg 50% 50%), hsl(60deg 50% 50%))") is "rgb(191, 170, 64)"
+PASS computedStyle("background-color", "color-mix(in hsl, hsl(60deg 50% 50%), hsl(40deg 50% 50%))") is "rgb(191, 170, 64)"
+PASS computedStyle("background-color", "color-mix(in hsl, hsl(50deg 50% 50%), hsl(330deg 50% 50%))") is "rgb(191, 85, 64)"
+PASS computedStyle("background-color", "color-mix(in hsl, hsl(330deg 50% 50%), hsl(50deg 50% 50%))") is "rgb(191, 85, 64)"
+PASS computedStyle("background-color", "color-mix(in hsl, hsl(20deg 50% 50%), hsl(320deg 50% 50%))") is "rgb(191, 64, 85)"
+PASS computedStyle("background-color", "color-mix(in hsl, hsl(320deg 50% 50%), hsl(20deg 50% 50%))") is "rgb(191, 64, 85)"
+-> shorter hue
+PASS computedStyle("background-color", "color-mix(in hsl shorter hue, hsl(40deg 50% 50%), hsl(60deg 50% 50%))") is "rgb(191, 170, 64)"
+PASS computedStyle("background-color", "color-mix(in hsl shorter hue, hsl(60deg 50% 50%), hsl(40deg 50% 50%))") is "rgb(191, 170, 64)"
+PASS computedStyle("background-color", "color-mix(in hsl shorter hue, hsl(50deg 50% 50%), hsl(330deg 50% 50%))") is "rgb(191, 85, 64)"
+PASS computedStyle("background-color", "color-mix(in hsl shorter hue, hsl(330deg 50% 50%), hsl(50deg 50% 50%))") is "rgb(191, 85, 64)"
+PASS computedStyle("background-color", "color-mix(in hsl shorter hue, hsl(20deg 50% 50%), hsl(320deg 50% 50%))") is "rgb(191, 64, 85)"
+PASS computedStyle("background-color", "color-mix(in hsl shorter hue, hsl(320deg 50% 50%), hsl(20deg 50% 50%))") is "rgb(191, 64, 85)"
+-> longer hue
+PASS computedStyle("background-color", "color-mix(in hsl longer hue, hsl(40deg 50% 50%), hsl(60deg 50% 50%))") is "rgb(64, 85, 191)"
+PASS computedStyle("background-color", "color-mix(in hsl longer hue, hsl(60deg 50% 50%), hsl(40deg 50% 50%))") is "rgb(64, 85, 191)"
+PASS computedStyle("background-color", "color-mix(in hsl longer hue, hsl(50deg 50% 50%), hsl(330deg 50% 50%))") is "rgb(64, 170, 191)"
+PASS computedStyle("background-color", "color-mix(in hsl longer hue, hsl(330deg 50% 50%), hsl(50deg 50% 50%))") is "rgb(64, 170, 191)"
+PASS computedStyle("background-color", "color-mix(in hsl longer hue, hsl(20deg 50% 50%), hsl(320deg 50% 50%))") is "rgb(64, 191, 170)"
+PASS computedStyle("background-color", "color-mix(in hsl longer hue, hsl(320deg 50% 50%), hsl(20deg 50% 50%))") is "rgb(64, 191, 170)"
+-> increasing hue
+PASS computedStyle("background-color", "color-mix(in hsl increasing hue, hsl(40deg 50% 50%), hsl(60deg 50% 50%))") is "rgb(191, 170, 64)"
+PASS computedStyle("background-color", "color-mix(in hsl increasing hue, hsl(60deg 50% 50%), hsl(40deg 50% 50%))") is "rgb(64, 85, 191)"
+PASS computedStyle("background-color", "color-mix(in hsl increasing hue, hsl(50deg 50% 50%), hsl(330deg 50% 50%))") is "rgb(64, 170, 191)"
+PASS computedStyle("background-color", "color-mix(in hsl increasing hue, hsl(330deg 50% 50%), hsl(50deg 50% 50%))") is "rgb(191, 85, 64)"
+PASS computedStyle("background-color", "color-mix(in hsl increasing hue, hsl(20deg 50% 50%), hsl(320deg 50% 50%))") is "rgb(64, 191, 170)"
+PASS computedStyle("background-color", "color-mix(in hsl increasing hue, hsl(320deg 50% 50%), hsl(20deg 50% 50%))") is "rgb(191, 64, 85)"
+-> decreasing hue
+PASS computedStyle("background-color", "color-mix(in hsl decreasing hue, hsl(40deg 50% 50%), hsl(60deg 50% 50%))") is "rgb(64, 85, 191)"
+PASS computedStyle("background-color", "color-mix(in hsl decreasing hue, hsl(60deg 50% 50%), hsl(40deg 50% 50%))") is "rgb(191, 170, 64)"
+PASS computedStyle("background-color", "color-mix(in hsl decreasing hue, hsl(50deg 50% 50%), hsl(330deg 50% 50%))") is "rgb(191, 85, 64)"
+PASS computedStyle("background-color", "color-mix(in hsl decreasing hue, hsl(330deg 50% 50%), hsl(50deg 50% 50%))") is "rgb(64, 170, 191)"
+PASS computedStyle("background-color", "color-mix(in hsl decreasing hue, hsl(20deg 50% 50%), hsl(320deg 50% 50%))") is "rgb(191, 64, 85)"
+PASS computedStyle("background-color", "color-mix(in hsl decreasing hue, hsl(320deg 50% 50%), hsl(20deg 50% 50%))") is "rgb(64, 191, 170)"
+-> specified hue
+PASS computedStyle("background-color", "color-mix(in hsl specified hue, hsl(40deg 50% 50%), hsl(60deg 50% 50%))") is "rgb(191, 170, 64)"
+PASS computedStyle("background-color", "color-mix(in hsl specified hue, hsl(60deg 50% 50%), hsl(40deg 50% 50%))") is "rgb(191, 170, 64)"
+PASS computedStyle("background-color", "color-mix(in hsl specified hue, hsl(50deg 50% 50%), hsl(330deg 50% 50%))") is "rgb(64, 170, 191)"
+PASS computedStyle("background-color", "color-mix(in hsl specified hue, hsl(330deg 50% 50%), hsl(50deg 50% 50%))") is "rgb(64, 170, 191)"
+PASS computedStyle("background-color", "color-mix(in hsl specified hue, hsl(20deg 50% 50%), hsl(320deg 50% 50%))") is "rgb(64, 191, 170)"
+PASS computedStyle("background-color", "color-mix(in hsl specified hue, hsl(320deg 50% 50%), hsl(20deg 50% 50%))") is "rgb(64, 191, 170)"
+-> Invalid examples
+PASS computedStyle("background-color", "color-mix(in hsl, hsl(120deg 10% 20%) -10%, hsl(30deg 30% 40%))") is "rgba(0, 0, 0, 0)"
+PASS computedStyle("background-color", "color-mix(in hsl, hsl(120deg 10% 20%) 150%, hsl(30deg 30% 40%))") is "rgba(0, 0, 0, 0)"
+PASS computedStyle("background-color", "color-mix(in hsl, hsl(120deg 10% 20%) 0%, hsl(30deg 30% 40%) 0%)") is "rgba(0, 0, 0, 0)"
 
 color-mix(in hwb, ...)
 PASS computedStyle("background-color", "color-mix(in hwb, hwb(120deg 10% 20%), hwb(30deg 30% 40%))") is "rgb(147, 179, 52)"
@@ -22,10 +67,55 @@
 PASS computedStyle("background-color", "color-mix(in hwb, hwb(120deg 10% 20%), 25% hwb(30deg 30% 40%))") is "rgb(96, 191, 39)"
 PASS computedStyle("background-color", "color-mix(in hwb, hwb(120deg 10% 20%), hwb(30deg 30% 40%) 25%)") is "rgb(96, 191, 39)"
 PASS computedStyle("background-color", "color-mix(in hwb, hwb(120deg 10% 20%) 25%, hwb(30deg 30% 40%) 75%)") is "rgb(166, 153, 64)"
-PASS computedStyle("background-color", "color-mix(in hwb, hwb(120deg 10% 20%) 50%, hwb(30deg 30% 40%) 150%)") is "rgb(166, 153, 64)"
-PASS computedStyle("background-color", "color-mix(in hwb, hwb(120deg 10% 20%) 12.5%, hwb(30deg 30% 40%) 37.5%)") is "rgb(166, 153, 64)"
+PASS computedStyle("background-color", "color-mix(in hwb, hwb(120deg 10% 20%) 30%, hwb(30deg 30% 40%) 90%)") is "rgb(166, 153, 64)"
+PASS computedStyle("background-color", "color-mix(in hwb, hwb(120deg 10% 20%) 12.5%, hwb(30deg 30% 40%) 37.5%)") is "rgba(166, 153, 64, 0.5)"
 PASS computedStyle("background-color", "color-mix(in hwb, hwb(120deg 10% 20%) 0%, hwb(30deg 30% 40%))") is "rgb(153, 115, 77)"
-PASS computedStyle("background-color", "color-mix(in hwb, hwb(120deg 10% 20%) -10%, hwb(30deg 30% 40%))") is "rgb(148, 105, 82)"
+-> no hue method specified, defaults to shorter
+PASS computedStyle("background-color", "color-mix(in hwb, hwb(40deg 30% 40%), hwb(60deg 30% 40%))") is "rgb(153, 140, 77)"
+PASS computedStyle("background-color", "color-mix(in hwb, hwb(60deg 30% 40%), hwb(40deg 30% 40%))") is "rgb(153, 140, 77)"
+PASS computedStyle("background-color", "color-mix(in hwb, hwb(50deg 30% 40%), hwb(330deg 30% 40%))") is "rgb(153, 89, 77)"
+PASS computedStyle("background-color", "color-mix(in hwb, hwb(330deg 30% 40%), hwb(50deg 30% 40%))") is "rgb(153, 89, 77)"
+PASS computedStyle("background-color", "color-mix(in hwb, hwb(20deg 30% 40%), hwb(320deg 30% 40%))") is "rgb(153, 77, 89)"
+PASS computedStyle("background-color", "color-mix(in hwb, hwb(320deg 30% 40%), hwb(20deg 30% 40%))") is "rgb(153, 77, 89)"
+-> shorter hue
+PASS computedStyle("background-color", "color-mix(in hwb shorter hue, hwb(40deg 30% 40%), hwb(60deg 30% 40%))") is "rgb(153, 140, 77)"
+PASS computedStyle("background-color", "color-mix(in hwb shorter hue, hwb(60deg 30% 40%), hwb(40deg 30% 40%))") is "rgb(153, 140, 77)"
+PASS computedStyle("background-color", "color-mix(in hwb shorter hue, hwb(50deg 30% 40%), hwb(330deg 30% 40%))") is "rgb(153, 89, 77)"
+PASS computedStyle("background-color", "color-mix(in hwb shorter hue, hwb(330deg 30% 40%), hwb(50deg 30% 40%))") is "rgb(153, 89, 77)"
+PASS computedStyle("background-color", "color-mix(in hwb shorter hue, hwb(20deg 30% 40%), hwb(320deg 30% 40%))") is "rgb(153, 77, 89)"
+PASS computedStyle("background-color", "color-mix(in hwb shorter hue, hwb(320deg 30% 40%), hwb(20deg 30% 40%))") is "rgb(153, 77, 89)"
+-> longer hue
+PASS computedStyle("background-color", "color-mix(in hwb longer hue, hwb(40deg 30% 40%), hwb(60deg 30% 40%))") is "rgb(77, 89, 153)"
+PASS computedStyle("background-color", "color-mix(in hwb longer hue, hwb(60deg 30% 40%), hwb(40deg 30% 40%))") is "rgb(77, 89, 153)"
+PASS computedStyle("background-color", "color-mix(in hwb longer hue, hwb(50deg 30% 40%), hwb(330deg 30% 40%))") is "rgb(77, 140, 153)"
+PASS computedStyle("background-color", "color-mix(in hwb longer hue, hwb(330deg 30% 40%), hwb(50deg 30% 40%))") is "rgb(77, 140, 153)"
+PASS computedStyle("background-color", "color-mix(in hwb longer hue, hwb(20deg 30% 40%), hwb(320deg 30% 40%))") is "rgb(77, 153, 140)"
+PASS computedStyle("background-color", "color-mix(in hwb longer hue, hwb(320deg 30% 40%), hwb(20deg 30% 40%))") is "rgb(77, 153, 140)"
+-> increasing hue
+PASS computedStyle("background-color", "color-mix(in hwb increasing hue, hwb(40deg 30% 40%), hwb(60deg 30% 40%))") is "rgb(153, 140, 77)"
+PASS computedStyle("background-color", "color-mix(in hwb increasing hue, hwb(60deg 30% 40%), hwb(40deg 30% 40%))") is "rgb(77, 89, 153)"
+PASS computedStyle("background-color", "color-mix(in hwb increasing hue, hwb(50deg 30% 40%), hwb(330deg 30% 40%))") is "rgb(77, 140, 153)"
+PASS computedStyle("background-color", "color-mix(in hwb increasing hue, hwb(330deg 30% 40%), hwb(50deg 30% 40%))") is "rgb(153, 89, 77)"
+PASS computedStyle("background-color", "color-mix(in hwb increasing hue, hwb(20deg 30% 40%), hwb(320deg 30% 40%))") is "rgb(77, 153, 140)"
+PASS computedStyle("background-color", "color-mix(in hwb increasing hue, hwb(320deg 30% 40%), hwb(20deg 30% 40%))") is "rgb(153, 77, 89)"
+-> decreasing hue
+PASS computedStyle("background-color", "color-mix(in hwb decreasing hue, hwb(40deg 30% 40%), hwb(60deg 30% 40%))") is "rgb(77, 89, 153)"
+PASS computedStyle("background-color", "color-mix(in hwb decreasing hue, hwb(60deg 30% 40%), hwb(40deg 30% 40%))") is "rgb(153, 140, 77)"
+PASS computedStyle("background-color", "color-mix(in hwb decreasing hue, hwb(50deg 30% 40%), hwb(330deg 30% 40%))") is "rgb(153, 89, 77)"
+PASS computedStyle("background-color", "color-mix(in hwb decreasing hue, hwb(330deg 30% 40%), hwb(50deg 30% 40%))") is "rgb(77, 140, 153)"
+PASS computedStyle("background-color", "color-mix(in hwb decreasing hue, hwb(20deg 30% 40%), hwb(320deg 30% 40%))") is "rgb(153, 77, 89)"
+PASS computedStyle("background-color", "color-mix(in hwb decreasing hue, hwb(320deg 30% 40%), hwb(20deg 30% 40%))") is "rgb(77, 153, 140)"
+-> specified hue
+PASS computedStyle("background-color", "color-mix(in hwb specified hue, hwb(40deg 30% 40%), hwb(60deg 30% 40%))") is "rgb(153, 140, 77)"
+PASS computedStyle("background-color", "color-mix(in hwb specified hue, hwb(60deg 30% 40%), hwb(40deg 30% 40%))") is "rgb(153, 140, 77)"
+PASS computedStyle("background-color", "color-mix(in hwb specified hue, hwb(50deg 30% 40%), hwb(330deg 30% 40%))") is "rgb(77, 140, 153)"
+PASS computedStyle("background-color", "color-mix(in hwb specified hue, hwb(330deg 30% 40%), hwb(50deg 30% 40%))") is "rgb(77, 140, 153)"
+PASS computedStyle("background-color", "color-mix(in hwb specified hue, hwb(20deg 30% 40%), hwb(320deg 30% 40%))") is "rgb(77, 153, 140)"
+PASS computedStyle("background-color", "color-mix(in hwb specified hue, hwb(320deg 30% 40%), hwb(20deg 30% 40%))") is "rgb(77, 153, 140)"
+-> Invalid examples
+PASS computedStyle("background-color", "color-mix(in hwb, hwb(120deg 10% 20%) -10%, hwb(30deg 30% 40%))") is "rgba(0, 0, 0, 0)"
+PASS computedStyle("background-color", "color-mix(in hwb, hwb(120deg 10% 20%) 150%, hwb(30deg 30% 40%))") is "rgba(0, 0, 0, 0)"
+PASS computedStyle("background-color", "color-mix(in hwb, hwb(120deg 10% 20%) 0%, hwb(30deg 30% 40%) 0%)") is "rgba(0, 0, 0, 0)"
 
 color-mix(in lch, ...)
 PASS computedStyle("background-color", "color-mix(in lch, lch(10% 20 30deg / .4), lch(50% 60 70deg / .8))") is "lch(30% 40 50 / 0.6)"
@@ -34,10 +124,55 @@
 PASS computedStyle("background-color", "color-mix(in lch, lch(10% 20 30deg / .4), 25% lch(50% 60 70deg / .8))") is "lch(20% 30 40 / 0.5)"
 PASS computedStyle("background-color", "color-mix(in lch, lch(10% 20 30deg / .4), lch(50% 60 70deg / .8) 25%)") is "lch(20% 30 40 / 0.5)"
 PASS computedStyle("background-color", "color-mix(in lch, lch(10% 20 30deg / .4) 25%, lch(50% 60 70deg / .8) 75%)") is "lch(40% 50 60 / 0.7)"
-PASS computedStyle("background-color", "color-mix(in lch, lch(10% 20 30deg / .4) 50%, lch(50% 60 70deg / .8) 150%)") is "lch(40% 50 60 / 0.7)"
-PASS computedStyle("background-color", "color-mix(in lch, lch(10% 20 30deg / .4) 12.5%, lch(50% 60 70deg / .8) 37.5%)") is "lch(40% 50 60 / 0.7)"
+PASS computedStyle("background-color", "color-mix(in lch, lch(10% 20 30deg / .4) 30%, lch(50% 60 70deg / .8) 90%)") is "lch(40% 50 60 / 0.7)"
+PASS computedStyle("background-color", "color-mix(in lch, lch(10% 20 30deg / .4) 12.5%, lch(50% 60 70deg / .8) 37.5%)") is "lch(40% 50 60 / 0.35)"
 PASS computedStyle("background-color", "color-mix(in lch, lch(10% 20 30deg / .4) 0%, lch(50% 60 70deg / .8))") is "lch(50% 60 70 / 0.8)"
-PASS computedStyle("background-color", "color-mix(in lch, lch(10% 20 30deg / .4) -10%, lch(50% 60 70deg / .8))") is "lch(54% 64 74 / 0.84000003)"
+-> no hue method specified, defaults to shorter
+PASS computedStyle("background-color", "color-mix(in lch, lch(100% 0 40deg), lch(100% 0 60deg))") is "lch(100% 0 50)"
+PASS computedStyle("background-color", "color-mix(in lch, lch(100% 0 60deg), lch(100% 0 40deg))") is "lch(100% 0 50)"
+PASS computedStyle("background-color", "color-mix(in lch, lch(100% 0 50deg), lch(100% 0 330deg))") is "lch(100% 0 10)"
+PASS computedStyle("background-color", "color-mix(in lch, lch(100% 0 330deg), lch(100% 0 50deg))") is "lch(100% 0 10)"
+PASS computedStyle("background-color", "color-mix(in lch, lch(100% 0 20deg), lch(100% 0 320deg))") is "lch(100% 0 350)"
+PASS computedStyle("background-color", "color-mix(in lch, lch(100% 0 320deg), lch(100% 0 20deg))") is "lch(100% 0 350)"
+-> shorter hue
+PASS computedStyle("background-color", "color-mix(in lch shorter hue, lch(100% 0 40deg), lch(100% 0 60deg))") is "lch(100% 0 50)"
+PASS computedStyle("background-color", "color-mix(in lch shorter hue, lch(100% 0 60deg), lch(100% 0 40deg))") is "lch(100% 0 50)"
+PASS computedStyle("background-color", "color-mix(in lch shorter hue, lch(100% 0 50deg), lch(100% 0 330deg))") is "lch(100% 0 10)"
+PASS computedStyle("background-color", "color-mix(in lch shorter hue, lch(100% 0 330deg), lch(100% 0 50deg))") is "lch(100% 0 10)"
+PASS computedStyle("background-color", "color-mix(in lch shorter hue, lch(100% 0 20deg), lch(100% 0 320deg))") is "lch(100% 0 350)"
+PASS computedStyle("background-color", "color-mix(in lch shorter hue, lch(100% 0 320deg), lch(100% 0 20deg))") is "lch(100% 0 350)"
+-> longer hue
+PASS computedStyle("background-color", "color-mix(in lch longer hue, lch(100% 0 40deg), lch(100% 0 60deg))") is "lch(100% 0 230)"
+PASS computedStyle("background-color", "color-mix(in lch longer hue, lch(100% 0 60deg), lch(100% 0 40deg))") is "lch(100% 0 230)"
+PASS computedStyle("background-color", "color-mix(in lch longer hue, lch(100% 0 50deg), lch(100% 0 330deg))") is "lch(100% 0 190)"
+PASS computedStyle("background-color", "color-mix(in lch longer hue, lch(100% 0 330deg), lch(100% 0 50deg))") is "lch(100% 0 190)"
+PASS computedStyle("background-color", "color-mix(in lch longer hue, lch(100% 0 20deg), lch(100% 0 320deg))") is "lch(100% 0 170)"
+PASS computedStyle("background-color", "color-mix(in lch longer hue, lch(100% 0 320deg), lch(100% 0 20deg))") is "lch(100% 0 170)"
+-> increasing hue
+PASS computedStyle("background-color", "color-mix(in lch increasing hue, lch(100% 0 40deg), lch(100% 0 60deg))") is "lch(100% 0 50)"
+PASS computedStyle("background-color", "color-mix(in lch increasing hue, lch(100% 0 60deg), lch(100% 0 40deg))") is "lch(100% 0 230)"
+PASS computedStyle("background-color", "color-mix(in lch increasing hue, lch(100% 0 50deg), lch(100% 0 330deg))") is "lch(100% 0 190)"
+PASS computedStyle("background-color", "color-mix(in lch increasing hue, lch(100% 0 330deg), lch(100% 0 50deg))") is "lch(100% 0 10)"
+PASS computedStyle("background-color", "color-mix(in lch increasing hue, lch(100% 0 20deg), lch(100% 0 320deg))") is "lch(100% 0 170)"
+PASS computedStyle("background-color", "color-mix(in lch increasing hue, lch(100% 0 320deg), lch(100% 0 20deg))") is "lch(100% 0 350)"
+-> decreasing hue
+PASS computedStyle("background-color", "color-mix(in lch decreasing hue, lch(100% 0 40deg), lch(100% 0 60deg))") is "lch(100% 0 230)"
+PASS computedStyle("background-color", "color-mix(in lch decreasing hue, lch(100% 0 60deg), lch(100% 0 40deg))") is "lch(100% 0 50)"
+PASS computedStyle("background-color", "color-mix(in lch decreasing hue, lch(100% 0 50deg), lch(100% 0 330deg))") is "lch(100% 0 10)"
+PASS computedStyle("background-color", "color-mix(in lch decreasing hue, lch(100% 0 330deg), lch(100% 0 50deg))") is "lch(100% 0 190)"
+PASS computedStyle("background-color", "color-mix(in lch decreasing hue, lch(100% 0 20deg), lch(100% 0 320deg))") is "lch(100% 0 350)"
+PASS computedStyle("background-color", "color-mix(in lch decreasing hue, lch(100% 0 320deg), lch(100% 0 20deg))") is "lch(100% 0 170)"
+-> specified hue
+PASS computedStyle("background-color", "color-mix(in lch specified hue, lch(100% 0 40deg), lch(100% 0 60deg))") is "lch(100% 0 50)"
+PASS computedStyle("background-color", "color-mix(in lch specified hue, lch(100% 0 60deg), lch(100% 0 40deg))") is "lch(100% 0 50)"
+PASS computedStyle("background-color", "color-mix(in lch specified hue, lch(100% 0 50deg), lch(100% 0 330deg))") is "lch(100% 0 190)"
+PASS computedStyle("background-color", "color-mix(in lch specified hue, lch(100% 0 330deg), lch(100% 0 50deg))") is "lch(100% 0 190)"
+PASS computedStyle("background-color", "color-mix(in lch specified hue, lch(100% 0 20deg), lch(100% 0 320deg))") is "lch(100% 0 170)"
+PASS computedStyle("background-color", "color-mix(in lch specified hue, lch(100% 0 320deg), lch(100% 0 20deg))") is "lch(100% 0 170)"
+-> Invalid examples
+PASS computedStyle("background-color", "color-mix(in lch, lch(10% 20 30deg / .4) -10%, lch(50% 60 70deg / .8))") is "rgba(0, 0, 0, 0)"
+PASS computedStyle("background-color", "color-mix(in lch, lch(10% 20 30deg / .4) 150%, lch(50% 60 70deg / .8))") is "rgba(0, 0, 0, 0)"
+PASS computedStyle("background-color", "color-mix(in lch, lch(10% 20 30deg / .4) 0%, lch(50% 60 70deg / .8) 0%)") is "rgba(0, 0, 0, 0)"
 
 color-mix(in oklch, ...)
 PASS computedStyle("background-color", "color-mix(in oklch, oklch(10% 20 30deg / .4), oklch(50% 60 70deg / .8))") is "oklch(30% 40 50 / 0.6)"
@@ -46,10 +181,55 @@
 PASS computedStyle("background-color", "color-mix(in oklch, oklch(10% 20 30deg / .4), 25% oklch(50% 60 70deg / .8))") is "oklch(20% 30 40 / 0.5)"
 PASS computedStyle("background-color", "color-mix(in oklch, oklch(10% 20 30deg / .4), oklch(50% 60 70deg / .8) 25%)") is "oklch(20% 30 40 / 0.5)"
 PASS computedStyle("background-color", "color-mix(in oklch, oklch(10% 20 30deg / .4) 25%, oklch(50% 60 70deg / .8) 75%)") is "oklch(40% 50 60 / 0.7)"
-PASS computedStyle("background-color", "color-mix(in oklch, oklch(10% 20 30deg / .4) 50%, oklch(50% 60 70deg / .8) 150%)") is "oklch(40% 50 60 / 0.7)"
-PASS computedStyle("background-color", "color-mix(in oklch, oklch(10% 20 30deg / .4) 12.5%, oklch(50% 60 70deg / .8) 37.5%)") is "oklch(40% 50 60 / 0.7)"
+PASS computedStyle("background-color", "color-mix(in oklch, oklch(10% 20 30deg / .4) 30%, oklch(50% 60 70deg / .8) 90%)") is "oklch(40% 50 60 / 0.7)"
+PASS computedStyle("background-color", "color-mix(in oklch, oklch(10% 20 30deg / .4) 12.5%, oklch(50% 60 70deg / .8) 37.5%)") is "oklch(40% 50 60 / 0.35)"
 PASS computedStyle("background-color", "color-mix(in oklch, oklch(10% 20 30deg / .4) 0%, oklch(50% 60 70deg / .8))") is "oklch(50% 60 70 / 0.8)"
-PASS computedStyle("background-color", "color-mix(in oklch, oklch(10% 20 30deg / .4) -10%, oklch(50% 60 70deg / .8))") is "oklch(54% 64 74 / 0.84000003)"
+-> no hue method specified, defaults to shorter
+PASS computedStyle("background-color", "color-mix(in oklch, oklch(100% 0 40deg), oklch(100% 0 60deg))") is "oklch(100% 0 50)"
+PASS computedStyle("background-color", "color-mix(in oklch, oklch(100% 0 60deg), oklch(100% 0 40deg))") is "oklch(100% 0 50)"
+PASS computedStyle("background-color", "color-mix(in oklch, oklch(100% 0 50deg), oklch(100% 0 330deg))") is "oklch(100% 0 10)"
+PASS computedStyle("background-color", "color-mix(in oklch, oklch(100% 0 330deg), oklch(100% 0 50deg))") is "oklch(100% 0 10)"
+PASS computedStyle("background-color", "color-mix(in oklch, oklch(100% 0 20deg), oklch(100% 0 320deg))") is "oklch(100% 0 350)"
+PASS computedStyle("background-color", "color-mix(in oklch, oklch(100% 0 320deg), oklch(100% 0 20deg))") is "oklch(100% 0 350)"
+-> shorter hue
+PASS computedStyle("background-color", "color-mix(in oklch shorter hue, oklch(100% 0 40deg), oklch(100% 0 60deg))") is "oklch(100% 0 50)"
+PASS computedStyle("background-color", "color-mix(in oklch shorter hue, oklch(100% 0 60deg), oklch(100% 0 40deg))") is "oklch(100% 0 50)"
+PASS computedStyle("background-color", "color-mix(in oklch shorter hue, oklch(100% 0 50deg), oklch(100% 0 330deg))") is "oklch(100% 0 10)"
+PASS computedStyle("background-color", "color-mix(in oklch shorter hue, oklch(100% 0 330deg), oklch(100% 0 50deg))") is "oklch(100% 0 10)"
+PASS computedStyle("background-color", "color-mix(in oklch shorter hue, oklch(100% 0 20deg), oklch(100% 0 320deg))") is "oklch(100% 0 350)"
+PASS computedStyle("background-color", "color-mix(in oklch shorter hue, oklch(100% 0 320deg), oklch(100% 0 20deg))") is "oklch(100% 0 350)"
+-> longer hue
+PASS computedStyle("background-color", "color-mix(in oklch longer hue, oklch(100% 0 40deg), oklch(100% 0 60deg))") is "oklch(100% 0 230)"
+PASS computedStyle("background-color", "color-mix(in oklch longer hue, oklch(100% 0 60deg), oklch(100% 0 40deg))") is "oklch(100% 0 230)"
+PASS computedStyle("background-color", "color-mix(in oklch longer hue, oklch(100% 0 50deg), oklch(100% 0 330deg))") is "oklch(100% 0 190)"
+PASS computedStyle("background-color", "color-mix(in oklch longer hue, oklch(100% 0 330deg), oklch(100% 0 50deg))") is "oklch(100% 0 190)"
+PASS computedStyle("background-color", "color-mix(in oklch longer hue, oklch(100% 0 20deg), oklch(100% 0 320deg))") is "oklch(100% 0 170)"
+PASS computedStyle("background-color", "color-mix(in oklch longer hue, oklch(100% 0 320deg), oklch(100% 0 20deg))") is "oklch(100% 0 170)"
+-> increasing hue
+PASS computedStyle("background-color", "color-mix(in oklch increasing hue, oklch(100% 0 40deg), oklch(100% 0 60deg))") is "oklch(100% 0 50)"
+PASS computedStyle("background-color", "color-mix(in oklch increasing hue, oklch(100% 0 60deg), oklch(100% 0 40deg))") is "oklch(100% 0 230)"
+PASS computedStyle("background-color", "color-mix(in oklch increasing hue, oklch(100% 0 50deg), oklch(100% 0 330deg))") is "oklch(100% 0 190)"
+PASS computedStyle("background-color", "color-mix(in oklch increasing hue, oklch(100% 0 330deg), oklch(100% 0 50deg))") is "oklch(100% 0 10)"
+PASS computedStyle("background-color", "color-mix(in oklch increasing hue, oklch(100% 0 20deg), oklch(100% 0 320deg))") is "oklch(100% 0 170)"
+PASS computedStyle("background-color", "color-mix(in oklch increasing hue, oklch(100% 0 320deg), oklch(100% 0 20deg))") is "oklch(100% 0 350)"
+-> decreasing hue
+PASS computedStyle("background-color", "color-mix(in oklch decreasing hue, oklch(100% 0 40deg), oklch(100% 0 60deg))") is "oklch(100% 0 230)"
+PASS computedStyle("background-color", "color-mix(in oklch decreasing hue, oklch(100% 0 60deg), oklch(100% 0 40deg))") is "oklch(100% 0 50)"
+PASS computedStyle("background-color", "color-mix(in oklch decreasing hue, oklch(100% 0 50deg), oklch(100% 0 330deg))") is "oklch(100% 0 10)"
+PASS computedStyle("background-color", "color-mix(in oklch decreasing hue, oklch(100% 0 330deg), oklch(100% 0 50deg))") is "oklch(100% 0 190)"
+PASS computedStyle("background-color", "color-mix(in oklch decreasing hue, oklch(100% 0 20deg), oklch(100% 0 320deg))") is "oklch(100% 0 350)"
+PASS computedStyle("background-color", "color-mix(in oklch decreasing hue, oklch(100% 0 320deg), oklch(100% 0 20deg))") is "oklch(100% 0 170)"
+-> specified hue
+PASS computedStyle("background-color", "color-mix(in oklch specified hue, oklch(100% 0 40deg), oklch(100% 0 60deg))") is "oklch(100% 0 50)"
+PASS computedStyle("background-color", "color-mix(in oklch specified hue, oklch(100% 0 60deg), oklch(100% 0 40deg))") is "oklch(100% 0 50)"
+PASS computedStyle("background-color", "color-mix(in oklch specified hue, oklch(100% 0 50deg), oklch(100% 0 330deg))") is "oklch(100% 0 190)"
+PASS computedStyle("background-color", "color-mix(in oklch specified hue, oklch(100% 0 330deg), oklch(100% 0 50deg))") is "oklch(100% 0 190)"
+PASS computedStyle("background-color", "color-mix(in oklch specified hue, oklch(100% 0 20deg), oklch(100% 0 320deg))") is "oklch(100% 0 170)"
+PASS computedStyle("background-color", "color-mix(in oklch specified hue, oklch(100% 0 320deg), oklch(100% 0 20deg))") is "oklch(100% 0 170)"
+-> Invalid examples
+PASS computedStyle("background-color", "color-mix(in oklch, oklch(10% 20 30deg / .4) -10%, oklch(50% 60 70deg / .8))") is "rgba(0, 0, 0, 0)"
+PASS computedStyle("background-color", "color-mix(in oklch, oklch(10% 20 30deg / .4) 150%, oklch(50% 60 70deg / .8))") is "rgba(0, 0, 0, 0)"
+PASS computedStyle("background-color", "color-mix(in oklch, oklch(10% 20 30deg / .4) 0%, oklch(50% 60 70deg / .8) 0%)") is "rgba(0, 0, 0, 0)"
 
 color-mix(in lab, ...)
 PASS computedStyle("background-color", "color-mix(in lab, lab(10% 20 30 / .4), lab(50% 60 70 / .8))") is "lab(30% 40 50 / 0.6)"
@@ -58,10 +238,13 @@
 PASS computedStyle("background-color", "color-mix(in lab, lab(10% 20 30 / .4), 25% lab(50% 60 70 / .8))") is "lab(20% 30 40 / 0.5)"
 PASS computedStyle("background-color", "color-mix(in lab, lab(10% 20 30 / .4), lab(50% 60 70 / .8) 25%)") is "lab(20% 30 40 / 0.5)"
 PASS computedStyle("background-color", "color-mix(in lab, lab(10% 20 30 / .4) 25%, lab(50% 60 70 / .8) 75%)") is "lab(40% 50 60 / 0.7)"
-PASS computedStyle("background-color", "color-mix(in lab, lab(10% 20 30 / .4) 50%, lab(50% 60 70 / .8) 150%)") is "lab(40% 50 60 / 0.7)"
-PASS computedStyle("background-color", "color-mix(in lab, lab(10% 20 30 / .4) 12.5%, lab(50% 60 70 / .8) 37.5%)") is "lab(40% 50 60 / 0.7)"
+PASS computedStyle("background-color", "color-mix(in lab, lab(10% 20 30 / .4) 30%, lab(50% 60 70 / .8) 90%)") is "lab(40% 50 60 / 0.7)"
+PASS computedStyle("background-color", "color-mix(in lab, lab(10% 20 30 / .4) 12.5%, lab(50% 60 70 / .8) 37.5%)") is "lab(40% 50 60 / 0.35)"
 PASS computedStyle("background-color", "color-mix(in lab, lab(10% 20 30 / .4) 0%, lab(50% 60 70 / .8))") is "lab(50% 60 70 / 0.8)"
-PASS computedStyle("background-color", "color-mix(in lab, lab(10% 20 30 / .4) -10%, lab(50% 60 70 / .8))") is "lab(54% 64 74 / 0.84000003)"
+-> Invalid examples
+PASS computedStyle("background-color", "color-mix(in lab, lab(10% 20 30 / .4) -10%, lab(50% 60 70 / .8))") is "rgba(0, 0, 0, 0)"
+PASS computedStyle("background-color", "color-mix(in lab, lab(10% 20 30 / .4) 150%, lab(50% 60 70 / .8))") is "rgba(0, 0, 0, 0)"
+PASS computedStyle("background-color", "color-mix(in lab, lab(10% 20 30 / .4) 0%, lab(50% 60 70 / .8) 0%)") is "rgba(0, 0, 0, 0)"
 
 color-mix(in oklab, ...)
 PASS computedStyle("background-color", "color-mix(in oklab, oklab(10% 20 30 / .4), oklab(50% 60 70 / .8))") is "oklab(30% 40 50 / 0.6)"
@@ -70,10 +253,13 @@
 PASS computedStyle("background-color", "color-mix(in oklab, oklab(10% 20 30 / .4), 25% oklab(50% 60 70 / .8))") is "oklab(20% 30 40 / 0.5)"
 PASS computedStyle("background-color", "color-mix(in oklab, oklab(10% 20 30 / .4), oklab(50% 60 70 / .8) 25%)") is "oklab(20% 30 40 / 0.5)"
 PASS computedStyle("background-color", "color-mix(in oklab, oklab(10% 20 30 / .4) 25%, oklab(50% 60 70 / .8) 75%)") is "oklab(40% 50 60 / 0.7)"
-PASS computedStyle("background-color", "color-mix(in oklab, oklab(10% 20 30 / .4) 50%, oklab(50% 60 70 / .8) 150%)") is "oklab(40% 50 60 / 0.7)"
-PASS computedStyle("background-color", "color-mix(in oklab, oklab(10% 20 30 / .4) 12.5%, oklab(50% 60 70 / .8) 37.5%)") is "oklab(40% 50 60 / 0.7)"
+PASS computedStyle("background-color", "color-mix(in oklab, oklab(10% 20 30 / .4) 30%, oklab(50% 60 70 / .8) 90%)") is "oklab(40% 50 60 / 0.7)"
+PASS computedStyle("background-color", "color-mix(in oklab, oklab(10% 20 30 / .4) 12.5%, oklab(50% 60 70 / .8) 37.5%)") is "oklab(40% 50 60 / 0.35)"
 PASS computedStyle("background-color", "color-mix(in oklab, oklab(10% 20 30 / .4) 0%, oklab(50% 60 70 / .8))") is "oklab(50% 60 70 / 0.8)"
-PASS computedStyle("background-color", "color-mix(in oklab, oklab(10% 20 30 / .4) -10%, oklab(50% 60 70 / .8))") is "oklab(54% 64 74 / 0.84000003)"
+-> Invalid examples
+PASS computedStyle("background-color", "color-mix(in oklab, oklab(10% 20 30 / .4) -10%, oklab(50% 60 70 / .8))") is "rgba(0, 0, 0, 0)"
+PASS computedStyle("background-color", "color-mix(in oklab, oklab(10% 20 30 / .4) 150%, oklab(50% 60 70 / .8))") is "rgba(0, 0, 0, 0)"
+PASS computedStyle("background-color", "color-mix(in oklab, oklab(10% 20 30 / .4) 0%, oklab(50% 60 70 / .8) 0%)") is "rgba(0, 0, 0, 0)"
 
 color-mix(in srgb, ...)
 PASS computedStyle("background-color", "color-mix(in srgb, color(srgb .1 .2 .3 / .4), color(srgb .5 .6 .7 / .8))") is "color(srgb 0.3 0.4 0.5 / 0.6)"
@@ -82,10 +268,13 @@
 PASS computedStyle("background-color", "color-mix(in srgb, color(srgb .1 .2 .3 / .4), color(srgb .5 .6 .7 / .8) 25%)") is "color(srgb 0.2 0.3 0.4 / 0.5)"
 PASS computedStyle("background-color", "color-mix(in srgb, color(srgb .1 .2 .3 / .4), 25% color(srgb .5 .6 .7 / .8))") is "color(srgb 0.2 0.3 0.4 / 0.5)"
 PASS computedStyle("background-color", "color-mix(in srgb, color(srgb .1 .2 .3 / .4) 25%, color(srgb .5 .6 .7 / .8) 75%)") is "color(srgb 0.4 0.5 0.6 / 0.7)"
-PASS computedStyle("background-color", "color-mix(in srgb, color(srgb .1 .2 .3 / .4) 50%, color(srgb .5 .6 .7 / .8) 150%)") is "color(srgb 0.4 0.5 0.6 / 0.7)"
-PASS computedStyle("background-color", "color-mix(in srgb, color(srgb .1 .2 .3 / .4) 12.5%, color(srgb .5 .6 .7 / .8) 37.5%)") is "color(srgb 0.4 0.5 0.6 / 0.7)"
+PASS computedStyle("background-color", "color-mix(in srgb, color(srgb .1 .2 .3 / .4) 30%, color(srgb .5 .6 .7 / .8) 90%)") is "color(srgb 0.4 0.5 0.6 / 0.7)"
+PASS computedStyle("background-color", "color-mix(in srgb, color(srgb .1 .2 .3 / .4) 12.5%, color(srgb .5 .6 .7 / .8) 37.5%)") is "color(srgb 0.4 0.5 0.6 / 0.35)"
 PASS computedStyle("background-color", "color-mix(in srgb, color(srgb .1 .2 .3 / .4) 0%, color(srgb .5 .6 .7 / .8))") is "color(srgb 0.5 0.6 0.7 / 0.8)"
-PASS computedStyle("background-color", "color-mix(in srgb, color(srgb .1 .2 .3 / .4) -10%, color(srgb .5 .6 .7 / .8))") is "color(srgb 0.54 0.64000005 0.74 / 0.84000003)"
+-> Invalid examples
+PASS computedStyle("background-color", "color-mix(in srgb, color(srgb .1 .2 .3 / .4) -10%, color(srgb .5 .6 .7 / .8))") is "rgba(0, 0, 0, 0)"
+PASS computedStyle("background-color", "color-mix(in srgb, color(srgb .1 .2 .3 / .4) 150%, color(srgb .5 .6 .7 / .8))") is "rgba(0, 0, 0, 0)"
+PASS computedStyle("background-color", "color-mix(in srgb, color(srgb .1 .2 .3 / .4) 0%, color(srgb .5 .6 .7 / .8) 0%)") is "rgba(0, 0, 0, 0)"
 
 color-mix(in xyz, ...)
 PASS computedStyle("background-color", "color-mix(in xyz, color(xyz .1 .2 .3 / .4), color(xyz .5 .6 .7 / .8))") is "color(xyz-d65 0.3 0.4 0.5 / 0.6)"
@@ -94,10 +283,13 @@
 PASS computedStyle("background-color", "color-mix(in xyz, color(xyz .1 .2 .3 / .4), color(xyz .5 .6 .7 / .8) 25%)") is "color(xyz-d65 0.2 0.3 0.4 / 0.5)"
 PASS computedStyle("background-color", "color-mix(in xyz, color(xyz .1 .2 .3 / .4), 25% color(xyz .5 .6 .7 / .8))") is "color(xyz-d65 0.2 0.3 0.4 / 0.5)"
 PASS computedStyle("background-color", "color-mix(in xyz, color(xyz .1 .2 .3 / .4) 25%, color(xyz .5 .6 .7 / .8) 75%)") is "color(xyz-d65 0.4 0.5 0.6 / 0.7)"
-PASS computedStyle("background-color", "color-mix(in xyz, color(xyz .1 .2 .3 / .4) 50%, color(xyz .5 .6 .7 / .8) 150%)") is "color(xyz-d65 0.4 0.5 0.6 / 0.7)"
-PASS computedStyle("background-color", "color-mix(in xyz, color(xyz .1 .2 .3 / .4) 12.5%, color(xyz .5 .6 .7 / .8) 37.5%)") is "color(xyz-d65 0.4 0.5 0.6 / 0.7)"
+PASS computedStyle("background-color", "color-mix(in xyz, color(xyz .1 .2 .3 / .4) 30%, color(xyz .5 .6 .7 / .8) 90%)") is "color(xyz-d65 0.4 0.5 0.6 / 0.7)"
+PASS computedStyle("background-color", "color-mix(in xyz, color(xyz .1 .2 .3 / .4) 12.5%, color(xyz .5 .6 .7 / .8) 37.5%)") is "color(xyz-d65 0.4 0.5 0.6 / 0.35)"
 PASS computedStyle("background-color", "color-mix(in xyz, color(xyz .1 .2 .3 / .4) 0%, color(xyz .5 .6 .7 / .8))") is "color(xyz-d65 0.5 0.6 0.7 / 0.8)"
-PASS computedStyle("background-color", "color-mix(in xyz, color(xyz .1 .2 .3 / .4) -10%, color(xyz .5 .6 .7 / .8))") is "color(xyz-d65 0.54 0.64000005 0.74 / 0.84000003)"
+-> Invalid examples
+PASS computedStyle("background-color", "color-mix(in xyz, color(xyz .1 .2 .3 / .4) -10%, color(xyz .5 .6 .7 / .8))") is "rgba(0, 0, 0, 0)"
+PASS computedStyle("background-color", "color-mix(in xyz, color(xyz .1 .2 .3 / .4) 150%, color(xyz .5 .6 .7 / .8))") is "rgba(0, 0, 0, 0)"
+PASS computedStyle("background-color", "color-mix(in xyz, color(xyz .1 .2 .3 / .4) 0%, color(xyz .5 .6 .7 / .8) 0%)") is "rgba(0, 0, 0, 0)"
 
 color-mix(in xyz-d50, ...)
 PASS computedStyle("background-color", "color-mix(in xyz-d50, color(xyz-d50 .1 .2 .3 / .4), color(xyz-d50 .5 .6 .7 / .8))") is "color(xyz-d50 0.3 0.4 0.5 / 0.6)"
@@ -106,10 +298,13 @@
 PASS computedStyle("background-color", "color-mix(in xyz-d50, color(xyz-d50 .1 .2 .3 / .4), color(xyz-d50 .5 .6 .7 / .8) 25%)") is "color(xyz-d50 0.2 0.3 0.4 / 0.5)"
 PASS computedStyle("background-color", "color-mix(in xyz-d50, color(xyz-d50 .1 .2 .3 / .4), 25% color(xyz-d50 .5 .6 .7 / .8))") is "color(xyz-d50 0.2 0.3 0.4 / 0.5)"
 PASS computedStyle("background-color", "color-mix(in xyz-d50, color(xyz-d50 .1 .2 .3 / .4) 25%, color(xyz-d50 .5 .6 .7 / .8) 75%)") is "color(xyz-d50 0.4 0.5 0.6 / 0.7)"
-PASS computedStyle("background-color", "color-mix(in xyz-d50, color(xyz-d50 .1 .2 .3 / .4) 50%, color(xyz-d50 .5 .6 .7 / .8) 150%)") is "color(xyz-d50 0.4 0.5 0.6 / 0.7)"
-PASS computedStyle("background-color", "color-mix(in xyz-d50, color(xyz-d50 .1 .2 .3 / .4) 12.5%, color(xyz-d50 .5 .6 .7 / .8) 37.5%)") is "color(xyz-d50 0.4 0.5 0.6 / 0.7)"
+PASS computedStyle("background-color", "color-mix(in xyz-d50, color(xyz-d50 .1 .2 .3 / .4) 30%, color(xyz-d50 .5 .6 .7 / .8) 90%)") is "color(xyz-d50 0.4 0.5 0.6 / 0.7)"
+PASS computedStyle("background-color", "color-mix(in xyz-d50, color(xyz-d50 .1 .2 .3 / .4) 12.5%, color(xyz-d50 .5 .6 .7 / .8) 37.5%)") is "color(xyz-d50 0.4 0.5 0.6 / 0.35)"
 PASS computedStyle("background-color", "color-mix(in xyz-d50, color(xyz-d50 .1 .2 .3 / .4) 0%, color(xyz-d50 .5 .6 .7 / .8))") is "color(xyz-d50 0.5 0.6 0.7 / 0.8)"
-PASS computedStyle("background-color", "color-mix(in xyz-d50, color(xyz-d50 .1 .2 .3 / .4) -10%, color(xyz-d50 .5 .6 .7 / .8))") is "color(xyz-d50 0.54 0.64000005 0.74 / 0.84000003)"
+-> Invalid examples
+PASS computedStyle("background-color", "color-mix(in xyz-d50, color(xyz-d50 .1 .2 .3 / .4) -10%, color(xyz-d50 .5 .6 .7 / .8))") is "rgba(0, 0, 0, 0)"
+PASS computedStyle("background-color", "color-mix(in xyz-d50, color(xyz-d50 .1 .2 .3 / .4) 150%, color(xyz-d50 .5 .6 .7 / .8))") is "rgba(0, 0, 0, 0)"
+PASS computedStyle("background-color", "color-mix(in xyz-d50, color(xyz-d50 .1 .2 .3 / .4) 0%, color(xyz-d50 .5 .6 .7 / .8) 0%)") is "rgba(0, 0, 0, 0)"
 
 color-mix(in xyz-d65, ...)
 PASS computedStyle("background-color", "color-mix(in xyz-d65, color(xyz-d65 .1 .2 .3 / .4), color(xyz-d65 .5 .6 .7 / .8))") is "color(xyz-d65 0.3 0.4 0.5 / 0.6)"
@@ -118,10 +313,13 @@
 PASS computedStyle("background-color", "color-mix(in xyz-d65, color(xyz-d65 .1 .2 .3 / .4), color(xyz-d65 .5 .6 .7 / .8) 25%)") is "color(xyz-d65 0.2 0.3 0.4 / 0.5)"
 PASS computedStyle("background-color", "color-mix(in xyz-d65, color(xyz-d65 .1 .2 .3 / .4), 25% color(xyz-d65 .5 .6 .7 / .8))") is "color(xyz-d65 0.2 0.3 0.4 / 0.5)"
 PASS computedStyle("background-color", "color-mix(in xyz-d65, color(xyz-d65 .1 .2 .3 / .4) 25%, color(xyz-d65 .5 .6 .7 / .8) 75%)") is "color(xyz-d65 0.4 0.5 0.6 / 0.7)"
-PASS computedStyle("background-color", "color-mix(in xyz-d65, color(xyz-d65 .1 .2 .3 / .4) 50%, color(xyz-d65 .5 .6 .7 / .8) 150%)") is "color(xyz-d65 0.4 0.5 0.6 / 0.7)"
-PASS computedStyle("background-color", "color-mix(in xyz-d65, color(xyz-d65 .1 .2 .3 / .4) 12.5%, color(xyz-d65 .5 .6 .7 / .8) 37.5%)") is "color(xyz-d65 0.4 0.5 0.6 / 0.7)"
+PASS computedStyle("background-color", "color-mix(in xyz-d65, color(xyz-d65 .1 .2 .3 / .4) 30%, color(xyz-d65 .5 .6 .7 / .8) 90%)") is "color(xyz-d65 0.4 0.5 0.6 / 0.7)"
+PASS computedStyle("background-color", "color-mix(in xyz-d65, color(xyz-d65 .1 .2 .3 / .4) 12.5%, color(xyz-d65 .5 .6 .7 / .8) 37.5%)") is "color(xyz-d65 0.4 0.5 0.6 / 0.35)"
 PASS computedStyle("background-color", "color-mix(in xyz-d65, color(xyz-d65 .1 .2 .3 / .4) 0%, color(xyz-d65 .5 .6 .7 / .8))") is "color(xyz-d65 0.5 0.6 0.7 / 0.8)"
-PASS computedStyle("background-color", "color-mix(in xyz-d65, color(xyz-d65 .1 .2 .3 / .4) -10%, color(xyz-d65 .5 .6 .7 / .8))") is "color(xyz-d65 0.54 0.64000005 0.74 / 0.84000003)"
+-> Invalid examples
+PASS computedStyle("background-color", "color-mix(in xyz-d65, color(xyz-d65 .1 .2 .3 / .4) -10%, color(xyz-d65 .5 .6 .7 / .8))") is "rgba(0, 0, 0, 0)"
+PASS computedStyle("background-color", "color-mix(in xyz-d65, color(xyz-d65 .1 .2 .3 / .4) 150%, color(xyz-d65 .5 .6 .7 / .8))") is "rgba(0, 0, 0, 0)"
+PASS computedStyle("background-color", "color-mix(in xyz-d65, color(xyz-d65 .1 .2 .3 / .4) 0%, color(xyz-d65 .5 .6 .7 / .8) 0%)") is "rgba(0, 0, 0, 0)"
 PASS successfullyParsed is true
 
 TEST COMPLETE

Modified: trunk/LayoutTests/fast/css/parsing-color-mix.html (286195 => 286196)


--- trunk/LayoutTests/fast/css/parsing-color-mix.html	2021-11-28 18:32:59 UTC (rev 286195)
+++ trunk/LayoutTests/fast/css/parsing-color-mix.html	2021-11-28 18:55:05 UTC (rev 286196)
@@ -26,6 +26,12 @@
         testComputedProperty("background-color", value, expected);
     }
 
+    function testComputedComputingResult(value, expected)
+    {
+        let computedExpected = computedStyle("background-color", expected)
+        testComputedProperty("background-color", value, computedExpected);
+    }
+
     debug('color-mix(in hsl, ...)');
 
     testComputed(`color-mix(in hsl, hsl(120deg 10% 20%), hsl(30deg 30% 40%))`, `rgb(84, 92, 61)`); // hsl(75deg 20% 30%)
@@ -34,14 +40,63 @@
     testComputed(`color-mix(in hsl, hsl(120deg 10% 20%), 25% hsl(30deg 30% 40%))`, `rgb(61, 73, 54)`);
     testComputed(`color-mix(in hsl, hsl(120deg 10% 20%), hsl(30deg 30% 40%) 25%)`, `rgb(61, 73, 54)`);
     testComputed(`color-mix(in hsl, hsl(120deg 10% 20%) 25%, hsl(30deg 30% 40%) 75%)`, `rgb(112, 106, 67)`);
-    testComputed(`color-mix(in hsl, hsl(120deg 10% 20%) 50%, hsl(30deg 30% 40%) 150%)`, `rgb(112, 106, 67)`); // Scale down > 100% sum.
-    testComputed(`color-mix(in hsl, hsl(120deg 10% 20%) 12.5%, hsl(30deg 30% 40%) 37.5%)`, `rgb(112, 106, 67)`); // Scale up < 100% sum.
+    testComputed(`color-mix(in hsl, hsl(120deg 10% 20%) 30%, hsl(30deg 30% 40%) 90%)`, `rgb(112, 106, 67)`); // Scale down > 100% sum.
+    testComputed(`color-mix(in hsl, hsl(120deg 10% 20%) 12.5%, hsl(30deg 30% 40%) 37.5%)`, `rgba(112, 106, 67, 0.5)`); // Scale up < 100% sum, causes alpha multiplication.
     testComputed(`color-mix(in hsl, hsl(120deg 10% 20%) 0%, hsl(30deg 30% 40%))`, `rgb(133, 102, 71)`);
 
-    // What should happen if you provide a negative percent? https://github.com/w3c/csswg-drafts/issues/6047
-    testComputed(`color-mix(in hsl, hsl(120deg 10% 20%) -10%, hsl(30deg 30% 40%))`, `rgb(142, 97, 72)`);
+    debug(`-> no hue method specified, defaults to shorter`)
+    testComputedComputingResult(`color-mix(in hsl, hsl(40deg 50% 50%), hsl(60deg 50% 50%))`, `hsl(50deg 50% 50%)`);
+    testComputedComputingResult(`color-mix(in hsl, hsl(60deg 50% 50%), hsl(40deg 50% 50%))`, `hsl(50deg 50% 50%)`);
+    testComputedComputingResult(`color-mix(in hsl, hsl(50deg 50% 50%), hsl(330deg 50% 50%))`, `hsl(10deg 50% 50%)`);
+    testComputedComputingResult(`color-mix(in hsl, hsl(330deg 50% 50%), hsl(50deg 50% 50%))`, `hsl(10deg 50% 50%)`);
+    testComputedComputingResult(`color-mix(in hsl, hsl(20deg 50% 50%), hsl(320deg 50% 50%))`, `hsl(350deg 50% 50%)`);
+    testComputedComputingResult(`color-mix(in hsl, hsl(320deg 50% 50%), hsl(20deg 50% 50%))`, `hsl(350deg 50% 50%)`);
 
+    debug(`-> shorter hue`)
+    testComputedComputingResult(`color-mix(in hsl shorter hue, hsl(40deg 50% 50%), hsl(60deg 50% 50%))`, `hsl(50deg 50% 50%)`);
+    testComputedComputingResult(`color-mix(in hsl shorter hue, hsl(60deg 50% 50%), hsl(40deg 50% 50%))`, `hsl(50deg 50% 50%)`);
+    testComputedComputingResult(`color-mix(in hsl shorter hue, hsl(50deg 50% 50%), hsl(330deg 50% 50%))`, `hsl(10deg 50% 50%)`);
+    testComputedComputingResult(`color-mix(in hsl shorter hue, hsl(330deg 50% 50%), hsl(50deg 50% 50%))`, `hsl(10deg 50% 50%)`);
+    testComputedComputingResult(`color-mix(in hsl shorter hue, hsl(20deg 50% 50%), hsl(320deg 50% 50%))`, `hsl(350deg 50% 50%)`);
+    testComputedComputingResult(`color-mix(in hsl shorter hue, hsl(320deg 50% 50%), hsl(20deg 50% 50%))`, `hsl(350deg 50% 50%)`);
 
+    debug(`-> longer hue`)
+    testComputedComputingResult(`color-mix(in hsl longer hue, hsl(40deg 50% 50%), hsl(60deg 50% 50%))`, `hsl(230deg 50% 50%)`);
+    testComputedComputingResult(`color-mix(in hsl longer hue, hsl(60deg 50% 50%), hsl(40deg 50% 50%))`, `hsl(230deg 50% 50%)`);
+    testComputedComputingResult(`color-mix(in hsl longer hue, hsl(50deg 50% 50%), hsl(330deg 50% 50%))`, `hsl(190deg 50% 50%)`);
+    testComputedComputingResult(`color-mix(in hsl longer hue, hsl(330deg 50% 50%), hsl(50deg 50% 50%))`, `hsl(190deg 50% 50%)`);
+    testComputedComputingResult(`color-mix(in hsl longer hue, hsl(20deg 50% 50%), hsl(320deg 50% 50%))`, `hsl(170deg 50% 50%)`);
+    testComputedComputingResult(`color-mix(in hsl longer hue, hsl(320deg 50% 50%), hsl(20deg 50% 50%))`, `hsl(170deg 50% 50%)`);
+
+    debug(`-> increasing hue`)
+    testComputedComputingResult(`color-mix(in hsl increasing hue, hsl(40deg 50% 50%), hsl(60deg 50% 50%))`, `hsl(50deg 50% 50%)`);
+    testComputedComputingResult(`color-mix(in hsl increasing hue, hsl(60deg 50% 50%), hsl(40deg 50% 50%))`, `hsl(230deg 50% 50%)`);
+    testComputedComputingResult(`color-mix(in hsl increasing hue, hsl(50deg 50% 50%), hsl(330deg 50% 50%))`, `hsl(190deg 50% 50%)`);
+    testComputedComputingResult(`color-mix(in hsl increasing hue, hsl(330deg 50% 50%), hsl(50deg 50% 50%))`, `hsl(10deg 50% 50%)`);
+    testComputedComputingResult(`color-mix(in hsl increasing hue, hsl(20deg 50% 50%), hsl(320deg 50% 50%))`, `hsl(170deg 50% 50%)`);
+    testComputedComputingResult(`color-mix(in hsl increasing hue, hsl(320deg 50% 50%), hsl(20deg 50% 50%))`, `hsl(350deg 50% 50%)`);
+
+    debug(`-> decreasing hue`)
+    testComputedComputingResult(`color-mix(in hsl decreasing hue, hsl(40deg 50% 50%), hsl(60deg 50% 50%))`, `hsl(230deg 50% 50%)`);
+    testComputedComputingResult(`color-mix(in hsl decreasing hue, hsl(60deg 50% 50%), hsl(40deg 50% 50%))`, `hsl(50deg 50% 50%)`);
+    testComputedComputingResult(`color-mix(in hsl decreasing hue, hsl(50deg 50% 50%), hsl(330deg 50% 50%))`, `hsl(10deg 50% 50%)`);
+    testComputedComputingResult(`color-mix(in hsl decreasing hue, hsl(330deg 50% 50%), hsl(50deg 50% 50%))`, `hsl(190deg 50% 50%)`);
+    testComputedComputingResult(`color-mix(in hsl decreasing hue, hsl(20deg 50% 50%), hsl(320deg 50% 50%))`, `hsl(350deg 50% 50%)`);
+    testComputedComputingResult(`color-mix(in hsl decreasing hue, hsl(320deg 50% 50%), hsl(20deg 50% 50%))`, `hsl(170deg 50% 50%)`);
+
+    debug(`-> specified hue`)
+    testComputedComputingResult(`color-mix(in hsl specified hue, hsl(40deg 50% 50%), hsl(60deg 50% 50%))`, `hsl(50deg 50% 50%)`);
+    testComputedComputingResult(`color-mix(in hsl specified hue, hsl(60deg 50% 50%), hsl(40deg 50% 50%))`, `hsl(50deg 50% 50%)`);
+    testComputedComputingResult(`color-mix(in hsl specified hue, hsl(50deg 50% 50%), hsl(330deg 50% 50%))`, `hsl(190deg 50% 50%)`);
+    testComputedComputingResult(`color-mix(in hsl specified hue, hsl(330deg 50% 50%), hsl(50deg 50% 50%))`, `hsl(190deg 50% 50%)`);
+    testComputedComputingResult(`color-mix(in hsl specified hue, hsl(20deg 50% 50%), hsl(320deg 50% 50%))`, `hsl(170deg 50% 50%)`);
+    testComputedComputingResult(`color-mix(in hsl specified hue, hsl(320deg 50% 50%), hsl(20deg 50% 50%))`, `hsl(170deg 50% 50%)`);
+
+    debug('-> Invalid examples');
+    testComputed(`color-mix(in hsl, hsl(120deg 10% 20%) -10%, hsl(30deg 30% 40%))`, `rgba(0, 0, 0, 0)`); // Percentages less than 0 are not valid.
+    testComputed(`color-mix(in hsl, hsl(120deg 10% 20%) 150%, hsl(30deg 30% 40%))`, `rgba(0, 0, 0, 0)`); // Percentages greater than 100 are not valid.
+    testComputed(`color-mix(in hsl, hsl(120deg 10% 20%) 0%, hsl(30deg 30% 40%) 0%)`, `rgba(0, 0, 0, 0)`); // Sum of percengates cannot be 0%.
+
     debug('');
     debug('color-mix(in hwb, ...)');
 
@@ -51,12 +106,62 @@
     testComputed(`color-mix(in hwb, hwb(120deg 10% 20%), 25% hwb(30deg 30% 40%))`, `rgb(96, 191, 39)`);
     testComputed(`color-mix(in hwb, hwb(120deg 10% 20%), hwb(30deg 30% 40%) 25%)`, `rgb(96, 191, 39)`);
     testComputed(`color-mix(in hwb, hwb(120deg 10% 20%) 25%, hwb(30deg 30% 40%) 75%)`, `rgb(166, 153, 64)`);
-    testComputed(`color-mix(in hwb, hwb(120deg 10% 20%) 50%, hwb(30deg 30% 40%) 150%)`, `rgb(166, 153, 64)`); // Scale down > 100% sum.
-    testComputed(`color-mix(in hwb, hwb(120deg 10% 20%) 12.5%, hwb(30deg 30% 40%) 37.5%)`, `rgb(166, 153, 64)`); // Scale up < 100% sum.
+    testComputed(`color-mix(in hwb, hwb(120deg 10% 20%) 30%, hwb(30deg 30% 40%) 90%)`, `rgb(166, 153, 64)`); // Scale down > 100% sum.
+    testComputed(`color-mix(in hwb, hwb(120deg 10% 20%) 12.5%, hwb(30deg 30% 40%) 37.5%)`, `rgba(166, 153, 64, 0.5)`); // Scale up < 100% sum, causes alpha multiplication.
     testComputed(`color-mix(in hwb, hwb(120deg 10% 20%) 0%, hwb(30deg 30% 40%))`, `rgb(153, 115, 77)`);
 
-    // What should happen if you provide a negative percent? https://github.com/w3c/csswg-drafts/issues/6047
-    testComputed(`color-mix(in hwb, hwb(120deg 10% 20%) -10%, hwb(30deg 30% 40%))`, `rgb(148, 105, 82)`);
+    debug(`-> no hue method specified, defaults to shorter`)
+    testComputedComputingResult(`color-mix(in hwb, hwb(40deg 30% 40%), hwb(60deg 30% 40%))`, `hwb(50deg 30% 40%)`);
+    testComputedComputingResult(`color-mix(in hwb, hwb(60deg 30% 40%), hwb(40deg 30% 40%))`, `hwb(50deg 30% 40%)`);
+    testComputedComputingResult(`color-mix(in hwb, hwb(50deg 30% 40%), hwb(330deg 30% 40%))`, `hwb(10deg 30% 40%)`);
+    testComputedComputingResult(`color-mix(in hwb, hwb(330deg 30% 40%), hwb(50deg 30% 40%))`, `hwb(10deg 30% 40%)`);
+    testComputedComputingResult(`color-mix(in hwb, hwb(20deg 30% 40%), hwb(320deg 30% 40%))`, `hwb(350deg 30% 40%)`);
+    testComputedComputingResult(`color-mix(in hwb, hwb(320deg 30% 40%), hwb(20deg 30% 40%))`, `hwb(350deg 30% 40%)`);
+
+    debug(`-> shorter hue`)
+    testComputedComputingResult(`color-mix(in hwb shorter hue, hwb(40deg 30% 40%), hwb(60deg 30% 40%))`, `hwb(50deg 30% 40%)`);
+    testComputedComputingResult(`color-mix(in hwb shorter hue, hwb(60deg 30% 40%), hwb(40deg 30% 40%))`, `hwb(50deg 30% 40%)`);
+    testComputedComputingResult(`color-mix(in hwb shorter hue, hwb(50deg 30% 40%), hwb(330deg 30% 40%))`, `hwb(10deg 30% 40%)`);
+    testComputedComputingResult(`color-mix(in hwb shorter hue, hwb(330deg 30% 40%), hwb(50deg 30% 40%))`, `hwb(10deg 30% 40%)`);
+    testComputedComputingResult(`color-mix(in hwb shorter hue, hwb(20deg 30% 40%), hwb(320deg 30% 40%))`, `hwb(350deg 30% 40%)`);
+    testComputedComputingResult(`color-mix(in hwb shorter hue, hwb(320deg 30% 40%), hwb(20deg 30% 40%))`, `hwb(350deg 30% 40%)`);
+
+    debug(`-> longer hue`)
+    testComputedComputingResult(`color-mix(in hwb longer hue, hwb(40deg 30% 40%), hwb(60deg 30% 40%))`, `hwb(230deg 30% 40%)`);
+    testComputedComputingResult(`color-mix(in hwb longer hue, hwb(60deg 30% 40%), hwb(40deg 30% 40%))`, `hwb(230deg 30% 40%)`);
+    testComputedComputingResult(`color-mix(in hwb longer hue, hwb(50deg 30% 40%), hwb(330deg 30% 40%))`, `hwb(190deg 30% 40%)`);
+    testComputedComputingResult(`color-mix(in hwb longer hue, hwb(330deg 30% 40%), hwb(50deg 30% 40%))`, `hwb(190deg 30% 40%)`);
+    testComputedComputingResult(`color-mix(in hwb longer hue, hwb(20deg 30% 40%), hwb(320deg 30% 40%))`, `hwb(170deg 30% 40%)`);
+    testComputedComputingResult(`color-mix(in hwb longer hue, hwb(320deg 30% 40%), hwb(20deg 30% 40%))`, `hwb(170deg 30% 40%)`);
+
+    debug(`-> increasing hue`)
+    testComputedComputingResult(`color-mix(in hwb increasing hue, hwb(40deg 30% 40%), hwb(60deg 30% 40%))`, `hwb(50deg 30% 40%)`);
+    testComputedComputingResult(`color-mix(in hwb increasing hue, hwb(60deg 30% 40%), hwb(40deg 30% 40%))`, `hwb(230deg 30% 40%)`);
+    testComputedComputingResult(`color-mix(in hwb increasing hue, hwb(50deg 30% 40%), hwb(330deg 30% 40%))`, `hwb(190deg 30% 40%)`);
+    testComputedComputingResult(`color-mix(in hwb increasing hue, hwb(330deg 30% 40%), hwb(50deg 30% 40%))`, `hwb(10deg 30% 40%)`);
+    testComputedComputingResult(`color-mix(in hwb increasing hue, hwb(20deg 30% 40%), hwb(320deg 30% 40%))`, `hwb(170deg 30% 40%)`);
+    testComputedComputingResult(`color-mix(in hwb increasing hue, hwb(320deg 30% 40%), hwb(20deg 30% 40%))`, `hwb(350deg 30% 40%)`);
+
+    debug(`-> decreasing hue`)
+    testComputedComputingResult(`color-mix(in hwb decreasing hue, hwb(40deg 30% 40%), hwb(60deg 30% 40%))`, `hwb(230deg 30% 40%)`);
+    testComputedComputingResult(`color-mix(in hwb decreasing hue, hwb(60deg 30% 40%), hwb(40deg 30% 40%))`, `hwb(50deg 30% 40%)`);
+    testComputedComputingResult(`color-mix(in hwb decreasing hue, hwb(50deg 30% 40%), hwb(330deg 30% 40%))`, `hwb(10deg 30% 40%)`);
+    testComputedComputingResult(`color-mix(in hwb decreasing hue, hwb(330deg 30% 40%), hwb(50deg 30% 40%))`, `hwb(190deg 30% 40%)`);
+    testComputedComputingResult(`color-mix(in hwb decreasing hue, hwb(20deg 30% 40%), hwb(320deg 30% 40%))`, `hwb(350deg 30% 40%)`);
+    testComputedComputingResult(`color-mix(in hwb decreasing hue, hwb(320deg 30% 40%), hwb(20deg 30% 40%))`, `hwb(170deg 30% 40%)`);
+
+    debug(`-> specified hue`)
+    testComputedComputingResult(`color-mix(in hwb specified hue, hwb(40deg 30% 40%), hwb(60deg 30% 40%))`, `hwb(50deg 30% 40%)`);
+    testComputedComputingResult(`color-mix(in hwb specified hue, hwb(60deg 30% 40%), hwb(40deg 30% 40%))`, `hwb(50deg 30% 40%)`);
+    testComputedComputingResult(`color-mix(in hwb specified hue, hwb(50deg 30% 40%), hwb(330deg 30% 40%))`, `hwb(190deg 30% 40%)`);
+    testComputedComputingResult(`color-mix(in hwb specified hue, hwb(330deg 30% 40%), hwb(50deg 30% 40%))`, `hwb(190deg 30% 40%)`);
+    testComputedComputingResult(`color-mix(in hwb specified hue, hwb(20deg 30% 40%), hwb(320deg 30% 40%))`, `hwb(170deg 30% 40%)`);
+    testComputedComputingResult(`color-mix(in hwb specified hue, hwb(320deg 30% 40%), hwb(20deg 30% 40%))`, `hwb(170deg 30% 40%)`);
+
+    debug('-> Invalid examples');
+    testComputed(`color-mix(in hwb, hwb(120deg 10% 20%) -10%, hwb(30deg 30% 40%))`, `rgba(0, 0, 0, 0)`); // Percentages less than 0 are not valid.
+    testComputed(`color-mix(in hwb, hwb(120deg 10% 20%) 150%, hwb(30deg 30% 40%))`, `rgba(0, 0, 0, 0)`); // Percentages greater than 100 are not valid.
+    testComputed(`color-mix(in hwb, hwb(120deg 10% 20%) 0%, hwb(30deg 30% 40%) 0%)`, `rgba(0, 0, 0, 0)`); // Sum of percengates cannot be 0%.
     
     for (const colorSpace of [ "lch", "oklch" ]) {
         debug('');
@@ -68,12 +173,62 @@
         testComputed(`color-mix(in ${colorSpace}, ${colorSpace}(10% 20 30deg / .4), 25% ${colorSpace}(50% 60 70deg / .8))`, `${colorSpace}(20% 30 40 / 0.5)`);
         testComputed(`color-mix(in ${colorSpace}, ${colorSpace}(10% 20 30deg / .4), ${colorSpace}(50% 60 70deg / .8) 25%)`, `${colorSpace}(20% 30 40 / 0.5)`);
         testComputed(`color-mix(in ${colorSpace}, ${colorSpace}(10% 20 30deg / .4) 25%, ${colorSpace}(50% 60 70deg / .8) 75%)`, `${colorSpace}(40% 50 60 / 0.7)`);
-        testComputed(`color-mix(in ${colorSpace}, ${colorSpace}(10% 20 30deg / .4) 50%, ${colorSpace}(50% 60 70deg / .8) 150%)`, `${colorSpace}(40% 50 60 / 0.7)`); // Scale down > 100% sum.
-        testComputed(`color-mix(in ${colorSpace}, ${colorSpace}(10% 20 30deg / .4) 12.5%, ${colorSpace}(50% 60 70deg / .8) 37.5%)`, `${colorSpace}(40% 50 60 / 0.7)`); // Scale up < 100% sum.
+        testComputed(`color-mix(in ${colorSpace}, ${colorSpace}(10% 20 30deg / .4) 30%, ${colorSpace}(50% 60 70deg / .8) 90%)`, `${colorSpace}(40% 50 60 / 0.7)`); // Scale down > 100% sum.
+        testComputed(`color-mix(in ${colorSpace}, ${colorSpace}(10% 20 30deg / .4) 12.5%, ${colorSpace}(50% 60 70deg / .8) 37.5%)`, `${colorSpace}(40% 50 60 / 0.35)`); // Scale up < 100% sum
         testComputed(`color-mix(in ${colorSpace}, ${colorSpace}(10% 20 30deg / .4) 0%, ${colorSpace}(50% 60 70deg / .8))`, `${colorSpace}(50% 60 70 / 0.8)`);
 
-        // What should happen if you provide a negative percent? https://github.com/w3c/csswg-drafts/issues/6047
-        testComputed(`color-mix(in ${colorSpace}, ${colorSpace}(10% 20 30deg / .4) -10%, ${colorSpace}(50% 60 70deg / .8))`, `${colorSpace}(54% 64 74 / 0.84000003)`);
+        debug(`-> no hue method specified, defaults to shorter`)
+        testComputed(`color-mix(in ${colorSpace}, ${colorSpace}(100% 0 40deg), ${colorSpace}(100% 0 60deg))`, `${colorSpace}(100% 0 50)`);
+        testComputed(`color-mix(in ${colorSpace}, ${colorSpace}(100% 0 60deg), ${colorSpace}(100% 0 40deg))`, `${colorSpace}(100% 0 50)`);
+        testComputed(`color-mix(in ${colorSpace}, ${colorSpace}(100% 0 50deg), ${colorSpace}(100% 0 330deg))`, `${colorSpace}(100% 0 10)`);
+        testComputed(`color-mix(in ${colorSpace}, ${colorSpace}(100% 0 330deg), ${colorSpace}(100% 0 50deg))`, `${colorSpace}(100% 0 10)`);
+        testComputed(`color-mix(in ${colorSpace}, ${colorSpace}(100% 0 20deg), ${colorSpace}(100% 0 320deg))`, `${colorSpace}(100% 0 350)`);
+        testComputed(`color-mix(in ${colorSpace}, ${colorSpace}(100% 0 320deg), ${colorSpace}(100% 0 20deg))`, `${colorSpace}(100% 0 350)`);
+
+        debug(`-> shorter hue`)
+        testComputed(`color-mix(in ${colorSpace} shorter hue, ${colorSpace}(100% 0 40deg), ${colorSpace}(100% 0 60deg))`, `${colorSpace}(100% 0 50)`);
+        testComputed(`color-mix(in ${colorSpace} shorter hue, ${colorSpace}(100% 0 60deg), ${colorSpace}(100% 0 40deg))`, `${colorSpace}(100% 0 50)`);
+        testComputed(`color-mix(in ${colorSpace} shorter hue, ${colorSpace}(100% 0 50deg), ${colorSpace}(100% 0 330deg))`, `${colorSpace}(100% 0 10)`);
+        testComputed(`color-mix(in ${colorSpace} shorter hue, ${colorSpace}(100% 0 330deg), ${colorSpace}(100% 0 50deg))`, `${colorSpace}(100% 0 10)`);
+        testComputed(`color-mix(in ${colorSpace} shorter hue, ${colorSpace}(100% 0 20deg), ${colorSpace}(100% 0 320deg))`, `${colorSpace}(100% 0 350)`);
+        testComputed(`color-mix(in ${colorSpace} shorter hue, ${colorSpace}(100% 0 320deg), ${colorSpace}(100% 0 20deg))`, `${colorSpace}(100% 0 350)`);
+
+        debug(`-> longer hue`)
+        testComputed(`color-mix(in ${colorSpace} longer hue, ${colorSpace}(100% 0 40deg), ${colorSpace}(100% 0 60deg))`, `${colorSpace}(100% 0 230)`);
+        testComputed(`color-mix(in ${colorSpace} longer hue, ${colorSpace}(100% 0 60deg), ${colorSpace}(100% 0 40deg))`, `${colorSpace}(100% 0 230)`);
+        testComputed(`color-mix(in ${colorSpace} longer hue, ${colorSpace}(100% 0 50deg), ${colorSpace}(100% 0 330deg))`, `${colorSpace}(100% 0 190)`);
+        testComputed(`color-mix(in ${colorSpace} longer hue, ${colorSpace}(100% 0 330deg), ${colorSpace}(100% 0 50deg))`, `${colorSpace}(100% 0 190)`);
+        testComputed(`color-mix(in ${colorSpace} longer hue, ${colorSpace}(100% 0 20deg), ${colorSpace}(100% 0 320deg))`, `${colorSpace}(100% 0 170)`);
+        testComputed(`color-mix(in ${colorSpace} longer hue, ${colorSpace}(100% 0 320deg), ${colorSpace}(100% 0 20deg))`, `${colorSpace}(100% 0 170)`);
+
+        debug(`-> increasing hue`)
+        testComputed(`color-mix(in ${colorSpace} increasing hue, ${colorSpace}(100% 0 40deg), ${colorSpace}(100% 0 60deg))`, `${colorSpace}(100% 0 50)`);
+        testComputed(`color-mix(in ${colorSpace} increasing hue, ${colorSpace}(100% 0 60deg), ${colorSpace}(100% 0 40deg))`, `${colorSpace}(100% 0 230)`);
+        testComputed(`color-mix(in ${colorSpace} increasing hue, ${colorSpace}(100% 0 50deg), ${colorSpace}(100% 0 330deg))`, `${colorSpace}(100% 0 190)`);
+        testComputed(`color-mix(in ${colorSpace} increasing hue, ${colorSpace}(100% 0 330deg), ${colorSpace}(100% 0 50deg))`, `${colorSpace}(100% 0 10)`);
+        testComputed(`color-mix(in ${colorSpace} increasing hue, ${colorSpace}(100% 0 20deg), ${colorSpace}(100% 0 320deg))`, `${colorSpace}(100% 0 170)`);
+        testComputed(`color-mix(in ${colorSpace} increasing hue, ${colorSpace}(100% 0 320deg), ${colorSpace}(100% 0 20deg))`, `${colorSpace}(100% 0 350)`);
+
+        debug(`-> decreasing hue`)
+        testComputed(`color-mix(in ${colorSpace} decreasing hue, ${colorSpace}(100% 0 40deg), ${colorSpace}(100% 0 60deg))`, `${colorSpace}(100% 0 230)`);
+        testComputed(`color-mix(in ${colorSpace} decreasing hue, ${colorSpace}(100% 0 60deg), ${colorSpace}(100% 0 40deg))`, `${colorSpace}(100% 0 50)`);
+        testComputed(`color-mix(in ${colorSpace} decreasing hue, ${colorSpace}(100% 0 50deg), ${colorSpace}(100% 0 330deg))`, `${colorSpace}(100% 0 10)`);
+        testComputed(`color-mix(in ${colorSpace} decreasing hue, ${colorSpace}(100% 0 330deg), ${colorSpace}(100% 0 50deg))`, `${colorSpace}(100% 0 190)`);
+        testComputed(`color-mix(in ${colorSpace} decreasing hue, ${colorSpace}(100% 0 20deg), ${colorSpace}(100% 0 320deg))`, `${colorSpace}(100% 0 350)`);
+        testComputed(`color-mix(in ${colorSpace} decreasing hue, ${colorSpace}(100% 0 320deg), ${colorSpace}(100% 0 20deg))`, `${colorSpace}(100% 0 170)`);
+    
+        debug(`-> specified hue`)
+        testComputed(`color-mix(in ${colorSpace} specified hue, ${colorSpace}(100% 0 40deg), ${colorSpace}(100% 0 60deg))`, `${colorSpace}(100% 0 50)`);
+        testComputed(`color-mix(in ${colorSpace} specified hue, ${colorSpace}(100% 0 60deg), ${colorSpace}(100% 0 40deg))`, `${colorSpace}(100% 0 50)`);
+        testComputed(`color-mix(in ${colorSpace} specified hue, ${colorSpace}(100% 0 50deg), ${colorSpace}(100% 0 330deg))`, `${colorSpace}(100% 0 190)`);
+        testComputed(`color-mix(in ${colorSpace} specified hue, ${colorSpace}(100% 0 330deg), ${colorSpace}(100% 0 50deg))`, `${colorSpace}(100% 0 190)`);
+        testComputed(`color-mix(in ${colorSpace} specified hue, ${colorSpace}(100% 0 20deg), ${colorSpace}(100% 0 320deg))`, `${colorSpace}(100% 0 170)`);
+        testComputed(`color-mix(in ${colorSpace} specified hue, ${colorSpace}(100% 0 320deg), ${colorSpace}(100% 0 20deg))`, `${colorSpace}(100% 0 170)`);
+
+        debug('-> Invalid examples');
+        testComputed(`color-mix(in ${colorSpace}, ${colorSpace}(10% 20 30deg / .4) -10%, ${colorSpace}(50% 60 70deg / .8))`, `rgba(0, 0, 0, 0)`); // Percentages less than 0 are not valid.
+        testComputed(`color-mix(in ${colorSpace}, ${colorSpace}(10% 20 30deg / .4) 150%, ${colorSpace}(50% 60 70deg / .8))`, `rgba(0, 0, 0, 0)`); // Percentages greater than 100 are not valid.
+        testComputed(`color-mix(in ${colorSpace}, ${colorSpace}(10% 20 30deg / .4) 0%, ${colorSpace}(50% 60 70deg / .8) 0%)`, `rgba(0, 0, 0, 0)`); // Sum of percengates cannot be 0%.
     }
 
     for (const colorSpace of [ "lab", "oklab" ]) {
@@ -86,33 +241,37 @@
         testComputed(`color-mix(in ${colorSpace}, ${colorSpace}(10% 20 30 / .4), 25% ${colorSpace}(50% 60 70 / .8))`, `${colorSpace}(20% 30 40 / 0.5)`);
         testComputed(`color-mix(in ${colorSpace}, ${colorSpace}(10% 20 30 / .4), ${colorSpace}(50% 60 70 / .8) 25%)`, `${colorSpace}(20% 30 40 / 0.5)`);
         testComputed(`color-mix(in ${colorSpace}, ${colorSpace}(10% 20 30 / .4) 25%, ${colorSpace}(50% 60 70 / .8) 75%)`, `${colorSpace}(40% 50 60 / 0.7)`);
-        testComputed(`color-mix(in ${colorSpace}, ${colorSpace}(10% 20 30 / .4) 50%, ${colorSpace}(50% 60 70 / .8) 150%)`, `${colorSpace}(40% 50 60 / 0.7)`); // Scale down > 100% sum.
-        testComputed(`color-mix(in ${colorSpace}, ${colorSpace}(10% 20 30 / .4) 12.5%, ${colorSpace}(50% 60 70 / .8) 37.5%)`, `${colorSpace}(40% 50 60 / 0.7)`); // Scale up < 100% sum.
+        testComputed(`color-mix(in ${colorSpace}, ${colorSpace}(10% 20 30 / .4) 30%, ${colorSpace}(50% 60 70 / .8) 90%)`, `${colorSpace}(40% 50 60 / 0.7)`); // Scale down > 100% sum.
+        testComputed(`color-mix(in ${colorSpace}, ${colorSpace}(10% 20 30 / .4) 12.5%, ${colorSpace}(50% 60 70 / .8) 37.5%)`, `${colorSpace}(40% 50 60 / 0.35)`); // Scale up < 100% sum, causes alpha multiplication.
         testComputed(`color-mix(in ${colorSpace}, ${colorSpace}(10% 20 30 / .4) 0%, ${colorSpace}(50% 60 70 / .8))`, `${colorSpace}(50% 60 70 / 0.8)`);
 
-        // What should happen if you provide a negative percent? https://github.com/w3c/csswg-drafts/issues/6047
-        testComputed(`color-mix(in ${colorSpace}, ${colorSpace}(10% 20 30 / .4) -10%, ${colorSpace}(50% 60 70 / .8))`, `${colorSpace}(54% 64 74 / 0.84000003)`);
+        debug('-> Invalid examples');
+        testComputed(`color-mix(in ${colorSpace}, ${colorSpace}(10% 20 30 / .4) -10%, ${colorSpace}(50% 60 70 / .8))`, `rgba(0, 0, 0, 0)`); // Percentages less than 0 are not valid.
+        testComputed(`color-mix(in ${colorSpace}, ${colorSpace}(10% 20 30 / .4) 150%, ${colorSpace}(50% 60 70 / .8))`, `rgba(0, 0, 0, 0)`); // Percentages greater than 100 are not valid.
+        testComputed(`color-mix(in ${colorSpace}, ${colorSpace}(10% 20 30 / .4) 0%, ${colorSpace}(50% 60 70 / .8) 0%)`, `rgba(0, 0, 0, 0)`); // Sum of percengates cannot be 0%.
     }
- 
+
     for (const colorSpace of [ "srgb", "xyz", "xyz-d50", "xyz-d65" ]) {
         debug('');
         debug(`color-mix(in ${colorSpace}, ...)`);
   
-  	  	const resultColorSpace = colorSpace == "xyz" ? "xyz-d65" : colorSpace; 
+        const resultColorSpace = colorSpace == "xyz" ? "xyz-d65" : colorSpace; 
 
-	    testComputed(`color-mix(in ${colorSpace}, color(${colorSpace} .1 .2 .3 / .4), color(${colorSpace} .5 .6 .7 / .8))`, `color(${resultColorSpace} 0.3 0.4 0.5 / 0.6)`);
-	    testComputed(`color-mix(in ${colorSpace}, color(${colorSpace} .1 .2 .3 / .4) 25%, color(${colorSpace} .5 .6 .7 / .8))`, `color(${resultColorSpace} 0.4 0.5 0.6 / 0.7)`);
-	    testComputed(`color-mix(in ${colorSpace}, 25% color(${colorSpace} .1 .2 .3 / .4), color(${colorSpace} .5 .6 .7 / .8))`, `color(${resultColorSpace} 0.4 0.5 0.6 / 0.7)`);
-	    testComputed(`color-mix(in ${colorSpace}, color(${colorSpace} .1 .2 .3 / .4), color(${colorSpace} .5 .6 .7 / .8) 25%)`, `color(${resultColorSpace} 0.2 0.3 0.4 / 0.5)`);
-	    testComputed(`color-mix(in ${colorSpace}, color(${colorSpace} .1 .2 .3 / .4), 25% color(${colorSpace} .5 .6 .7 / .8))`, `color(${resultColorSpace} 0.2 0.3 0.4 / 0.5)`);
-	    testComputed(`color-mix(in ${colorSpace}, color(${colorSpace} .1 .2 .3 / .4) 25%, color(${colorSpace} .5 .6 .7 / .8) 75%)`, `color(${resultColorSpace} 0.4 0.5 0.6 / 0.7)`);
-	    testComputed(`color-mix(in ${colorSpace}, color(${colorSpace} .1 .2 .3 / .4) 50%, color(${colorSpace} .5 .6 .7 / .8) 150%)`, `color(${resultColorSpace} 0.4 0.5 0.6 / 0.7)`); // Scale down > 100% sum.
-	    testComputed(`color-mix(in ${colorSpace}, color(${colorSpace} .1 .2 .3 / .4) 12.5%, color(${colorSpace} .5 .6 .7 / .8) 37.5%)`, `color(${resultColorSpace} 0.4 0.5 0.6 / 0.7)`); // Scale up < 100% sum.
-	    testComputed(`color-mix(in ${colorSpace}, color(${colorSpace} .1 .2 .3 / .4) 0%, color(${colorSpace} .5 .6 .7 / .8))`, `color(${resultColorSpace} 0.5 0.6 0.7 / 0.8)`);
+        testComputed(`color-mix(in ${colorSpace}, color(${colorSpace} .1 .2 .3 / .4), color(${colorSpace} .5 .6 .7 / .8))`, `color(${resultColorSpace} 0.3 0.4 0.5 / 0.6)`);
+        testComputed(`color-mix(in ${colorSpace}, color(${colorSpace} .1 .2 .3 / .4) 25%, color(${colorSpace} .5 .6 .7 / .8))`, `color(${resultColorSpace} 0.4 0.5 0.6 / 0.7)`);
+        testComputed(`color-mix(in ${colorSpace}, 25% color(${colorSpace} .1 .2 .3 / .4), color(${colorSpace} .5 .6 .7 / .8))`, `color(${resultColorSpace} 0.4 0.5 0.6 / 0.7)`);
+        testComputed(`color-mix(in ${colorSpace}, color(${colorSpace} .1 .2 .3 / .4), color(${colorSpace} .5 .6 .7 / .8) 25%)`, `color(${resultColorSpace} 0.2 0.3 0.4 / 0.5)`);
+        testComputed(`color-mix(in ${colorSpace}, color(${colorSpace} .1 .2 .3 / .4), 25% color(${colorSpace} .5 .6 .7 / .8))`, `color(${resultColorSpace} 0.2 0.3 0.4 / 0.5)`);
+        testComputed(`color-mix(in ${colorSpace}, color(${colorSpace} .1 .2 .3 / .4) 25%, color(${colorSpace} .5 .6 .7 / .8) 75%)`, `color(${resultColorSpace} 0.4 0.5 0.6 / 0.7)`);
+        testComputed(`color-mix(in ${colorSpace}, color(${colorSpace} .1 .2 .3 / .4) 30%, color(${colorSpace} .5 .6 .7 / .8) 90%)`, `color(${resultColorSpace} 0.4 0.5 0.6 / 0.7)`); // Scale down > 100% sum.
+        testComputed(`color-mix(in ${colorSpace}, color(${colorSpace} .1 .2 .3 / .4) 12.5%, color(${colorSpace} .5 .6 .7 / .8) 37.5%)`, `color(${resultColorSpace} 0.4 0.5 0.6 / 0.35)`); // Scale up < 100% sum, causes alpha multiplication.
+        testComputed(`color-mix(in ${colorSpace}, color(${colorSpace} .1 .2 .3 / .4) 0%, color(${colorSpace} .5 .6 .7 / .8))`, `color(${resultColorSpace} 0.5 0.6 0.7 / 0.8)`);
 
-	    // What should happen if you provide a negative percent? https://github.com/w3c/csswg-drafts/issues/6047
-	    testComputed(`color-mix(in ${colorSpace}, color(${colorSpace} .1 .2 .3 / .4) -10%, color(${colorSpace} .5 .6 .7 / .8))`, `color(${resultColorSpace} 0.54 0.64000005 0.74 / 0.84000003)`);
-	}
+        debug('-> Invalid examples');
+        testComputed(`color-mix(in ${colorSpace}, color(${colorSpace} .1 .2 .3 / .4) -10%, color(${colorSpace} .5 .6 .7 / .8))`, `rgba(0, 0, 0, 0)`); // Percentages less than 0 are not valid.
+        testComputed(`color-mix(in ${colorSpace}, color(${colorSpace} .1 .2 .3 / .4) 150%, color(${colorSpace} .5 .6 .7 / .8))`, `rgba(0, 0, 0, 0)`); // Percentages greater than 100 are not valid.
+        testComputed(`color-mix(in ${colorSpace}, color(${colorSpace} .1 .2 .3 / .4) 0%, color(${colorSpace} .5 .6 .7 / .8) 0%)`, `rgba(0, 0, 0, 0)`); // Sum of percengates cannot be 0%.
+    }
 </script>
     
 <script src=""

Modified: trunk/Source/WebCore/ChangeLog (286195 => 286196)


--- trunk/Source/WebCore/ChangeLog	2021-11-28 18:32:59 UTC (rev 286195)
+++ trunk/Source/WebCore/ChangeLog	2021-11-28 18:55:05 UTC (rev 286196)
@@ -1,3 +1,48 @@
+2021-11-28  Sam Weinig  <wei...@apple.com>
+
+        [CSS Color 5] Update color-mix() to latest spec (again)
+        https://bugs.webkit.org/show_bug.cgi?id=233527
+
+        Reviewed by Dean Jackson.
+
+        Updates color-mix() implementation with support for hue interpolation methods and
+        addresses various other spec changes (detailed below).
+
+        * Sources.txt:
+        * WebCore.xcodeproj/project.pbxproj:
+        Add ColorInterpolation.h/cpp
+
+        * css/CSSValueKeywords.in:
+        Add keywords need for the <color-space-interpolation> production. Separated out
+        from color-mix as these will be used for other CSS properties as well.
+
+        * css/parser/CSSPropertyParserHelpers.cpp:
+        (WebCore::CSSPropertyParserHelpers::consumeHueInterpolationMethod):
+        (WebCore::CSSPropertyParserHelpers::consumeColorInterpolationMethod):
+        (WebCore::CSSPropertyParserHelpers::consumeColorMixComponent):
+        (WebCore::CSSPropertyParserHelpers::normalizedMixPercentages):
+        (WebCore::CSSPropertyParserHelpers::makeColorTypeByNormalizingComponentsAfterMix<HWBA<float>>):
+        (WebCore::CSSPropertyParserHelpers::makeColorTypeByNormalizingComponentsAfterMix<HSLA<float>>):
+        (WebCore::CSSPropertyParserHelpers::makeColorTypeByNormalizingComponentsAfterMix<LCHA<float>>):
+        (WebCore::CSSPropertyParserHelpers::makeColorTypeByNormalizingComponentsAfterMix<OKLCHA<float>>):
+        (WebCore::CSSPropertyParserHelpers::mixColorComponentsUsingColorInterpolationMethod):
+        (WebCore::CSSPropertyParserHelpers::mixColorComponents):
+        (WebCore::CSSPropertyParserHelpers::parseColorMixFunctionParameters):
+        (WebCore::CSSPropertyParserHelpers::consumeColorMixColorSpaceAndComma): Deleted.
+        (WebCore::CSSPropertyParserHelpers::fixupHueComponentsPriorToMix): Deleted.
+        (WebCore::CSSPropertyParserHelpers::mixColorComponentsInColorSpace): Deleted.
+        Rework color-mix to support the hue interpolation method specification for polor color spaces 
+        like lch, oklch, hsl and hwb. Also update implementation to match the current spec by restricting
+        mix percentages to between 0 and 100 and applying alpha multiplier when the mix percentages add
+        up to less than 100.
+
+        * platform/graphics/ColorInterpolation.cpp: Added.
+        * platform/graphics/ColorInterpolation.h: Added.
+        Add structures to represent color interpolation methods including optional hue
+        interpolation parameters for color spaces that require it and provides prenormalization
+        that fixes up hue angles depending on the method selected. Over time, more aspects of
+        interpolation should be moved here as we figure out what can be shared.
+
 2021-11-28  Alan Bujtas  <za...@apple.com>
 
         [LFC][IFC] Visual ordering may require multiple display box instances for a single inline box

Modified: trunk/Source/WebCore/Sources.txt (286195 => 286196)


--- trunk/Source/WebCore/Sources.txt	2021-11-28 18:32:59 UTC (rev 286195)
+++ trunk/Source/WebCore/Sources.txt	2021-11-28 18:55:05 UTC (rev 286196)
@@ -1983,6 +1983,7 @@
 platform/graphics/Color.cpp
 platform/graphics/ColorBlending.cpp
 platform/graphics/ColorConversion.cpp
+platform/graphics/ColorInterpolation.cpp
 platform/graphics/ColorLuminance.cpp
 platform/graphics/ColorSerialization.cpp
 platform/graphics/ColorSpace.cpp

Modified: trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj (286195 => 286196)


--- trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj	2021-11-28 18:32:59 UTC (rev 286195)
+++ trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj	2021-11-28 18:55:05 UTC (rev 286196)
@@ -15110,6 +15110,8 @@
 		BCD9C2BD0C17B69E005C90A2 /* JSNamedNodeMap.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = JSNamedNodeMap.h; sourceTree = "<group>"; };
 		BCD9C2BE0C17B69E005C90A2 /* JSNodeList.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = JSNodeList.cpp; sourceTree = "<group>"; };
 		BCD9C2BF0C17B69E005C90A2 /* JSNodeList.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = JSNodeList.h; sourceTree = "<group>"; };
+		BCDC642427517B040038FB39 /* ColorInterpolation.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ColorInterpolation.cpp; sourceTree = "<group>"; };
+		BCDC642527517B040038FB39 /* ColorInterpolation.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ColorInterpolation.h; sourceTree = "<group>"; };
 		BCDD454D1236C95C009A7985 /* ColumnInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ColumnInfo.h; sourceTree = "<group>"; };
 		BCDF317911F8D683003C5BF8 /* UserTypingGestureIndicator.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = UserTypingGestureIndicator.cpp; sourceTree = "<group>"; };
 		BCDF317A11F8D683003C5BF8 /* UserTypingGestureIndicator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UserTypingGestureIndicator.h; sourceTree = "<group>"; };
@@ -27934,6 +27936,8 @@
 				7C514E0224AF80580050710F /* ColorConversion.cpp */,
 				7C514E0024AF80580050710F /* ColorConversion.h */,
 				3103B7DE1DB01556008BB890 /* ColorHash.h */,
+				BCDC642427517B040038FB39 /* ColorInterpolation.cpp */,
+				BCDC642527517B040038FB39 /* ColorInterpolation.h */,
 				BC6EB84526266B61003225A7 /* ColorLuminance.cpp */,
 				BC4A23EB25EC160200AAC630 /* ColorLuminance.h */,
 				7CAC6AE8247F082000E61D59 /* ColorMatrix.h */,

Modified: trunk/Source/WebCore/css/CSSValueKeywords.in (286195 => 286196)


--- trunk/Source/WebCore/css/CSSValueKeywords.in	2021-11-28 18:32:59 UTC (rev 286195)
+++ trunk/Source/WebCore/css/CSSValueKeywords.in	2021-11-28 18:55:05 UTC (rev 286196)
@@ -1485,7 +1485,25 @@
 
 // color-mix()
 color-mix
+
+// color-space-interpolation
 in
+// srgb
+// lab
+// oklab
+// xyz
+// xyz-d50
+// xyz-d65
+// hsl
+// hwb
+// lch
+// oklch
+shorter
+longer
+increasing
+decreasing
+specified
+// hue
 
 // prefers-default-appearance
 prefers

Modified: trunk/Source/WebCore/css/parser/CSSPropertyParserHelpers.cpp (286195 => 286196)


--- trunk/Source/WebCore/css/parser/CSSPropertyParserHelpers.cpp	2021-11-28 18:32:59 UTC (rev 286195)
+++ trunk/Source/WebCore/css/parser/CSSPropertyParserHelpers.cpp	2021-11-28 18:55:05 UTC (rev 286196)
@@ -44,6 +44,7 @@
 #include "CSSValuePool.h"
 #include "CalculationCategory.h"
 #include "ColorConversion.h"
+#include "ColorInterpolation.h"
 #include "ColorLuminance.h"
 #include "Logging.h"
 #include "Pair.h"
@@ -2290,49 +2291,87 @@
     return selectFirstColorWithHighestContrast(originBackgroundColor, WTFMove(colorsToCompareAgainst));
 }
 
-enum class ColorMixColorSpace {
-    Hsl,
-    Hwb,
-    Lab,
-    Lch,
-    Oklab,
-    Oklch,
-    Srgb,
-    XyzD50,
-    XyzD65
-};
+static std::optional<HueInterpolationMethod> consumeHueInterpolationMethod(CSSParserTokenRange& args)
+{
+    switch (args.peek().id()) {
+    case CSSValueShorter:
+        args.consumeIncludingWhitespace();
+        return HueInterpolationMethod::Shorter;
+    case CSSValueLonger:
+        args.consumeIncludingWhitespace();
+        return HueInterpolationMethod::Longer;
+    case CSSValueIncreasing:
+        args.consumeIncludingWhitespace();
+        return HueInterpolationMethod::Increasing;
+    case CSSValueDecreasing:
+        args.consumeIncludingWhitespace();
+        return HueInterpolationMethod::Decreasing;
+    case CSSValueSpecified:
+        args.consumeIncludingWhitespace();
+        return HueInterpolationMethod::Specified;
+    default:
+        return { };
+    }
+}
 
-static std::optional<ColorMixColorSpace> consumeColorMixColorSpaceAndComma(CSSParserTokenRange& args)
+static std::optional<ColorInterpolationMethod> consumeColorInterpolationMethod(CSSParserTokenRange& args)
 {
-    auto consumeIdentAndComma = [](CSSParserTokenRange& args, ColorMixColorSpace colorSpace) -> std::optional<ColorMixColorSpace> {
-        consumeIdentRaw(args);
-        if (!consumeCommaIncludingWhitespace(args))
-            return std::nullopt;
-        return colorSpace;
+    // <rectangular-color-space> = srgb | lab | oklab | xyz | xyz-d50 | xyz-d65
+    // <polar-color-space> = hsl | hwb | lch | oklch
+    // <hue-interpolation-method> = [ shorter | longer | increasing | decreasing | specified ] hue
+    // <color-interpolation-method> = in [ <rectangular-color-space> | <polar-color-space> <hue-interpolation-method>? ]
+
+    if (!consumeIdentRaw<CSSValueIn>(args))
+        return { };
+
+    auto consumePolarColorSpace = [](CSSParserTokenRange& args, auto colorInterpolationMethod) -> std::optional<ColorInterpolationMethod> {
+        // Consume the color space identifier.
+        args.consumeIncludingWhitespace();
+
+        // <hue-interpolation-method> is optional, so if it is not provided, we just use the default value
+        // specified in the passed in 'colorInterpolationMethod' parameter.
+        auto hueInterpolationMethod = consumeHueInterpolationMethod(args);
+        if (!hueInterpolationMethod)
+            return {{ colorInterpolationMethod }};
+        
+        // If the hue-interpolation-method was provided it must be followed immediately by the 'hue' identifier.
+        if (!consumeIdentRaw<CSSValueHue>(args))
+            return { };
+
+        colorInterpolationMethod.hueInterpolationMethod = *hueInterpolationMethod;
+
+        return {{ colorInterpolationMethod }};
     };
 
+    auto consumeRectangularColorSpace = [](CSSParserTokenRange& args, auto colorInterpolationMethod) -> std::optional<ColorInterpolationMethod> {
+        // Consume the color space identifier.
+        args.consumeIncludingWhitespace();
+
+        return {{ colorInterpolationMethod }};
+    };
+
     switch (args.peek().id()) {
     case CSSValueHsl:
-        return consumeIdentAndComma(args, ColorMixColorSpace::Hsl);
+        return consumePolarColorSpace(args, ColorInterpolationMethod::HSL { });
     case CSSValueHwb:
-        return consumeIdentAndComma(args, ColorMixColorSpace::Hwb);
+        return consumePolarColorSpace(args, ColorInterpolationMethod::HWB { });
+    case CSSValueLch:
+        return consumePolarColorSpace(args, ColorInterpolationMethod::LCH { });
     case CSSValueLab:
-        return consumeIdentAndComma(args, ColorMixColorSpace::Lab);
-    case CSSValueLch:
-        return consumeIdentAndComma(args, ColorMixColorSpace::Lch);
+        return consumeRectangularColorSpace(args, ColorInterpolationMethod::Lab { });
+    case CSSValueOklch:
+        return consumePolarColorSpace(args, ColorInterpolationMethod::OKLCH { });
     case CSSValueOklab:
-        return consumeIdentAndComma(args, ColorMixColorSpace::Oklab);
-    case CSSValueOklch:
-        return consumeIdentAndComma(args, ColorMixColorSpace::Oklch);
+        return consumeRectangularColorSpace(args, ColorInterpolationMethod::OKLab { });
     case CSSValueSRGB:
-        return consumeIdentAndComma(args, ColorMixColorSpace::Srgb);
+        return consumeRectangularColorSpace(args, ColorInterpolationMethod::SRGB { });
     case CSSValueXyzD50:
-        return consumeIdentAndComma(args, ColorMixColorSpace::XyzD50);
+        return consumeRectangularColorSpace(args, ColorInterpolationMethod::XYZD50 { });
     case CSSValueXyz:
     case CSSValueXyzD65:
-        return consumeIdentAndComma(args, ColorMixColorSpace::XyzD65);
+        return consumeRectangularColorSpace(args, ColorInterpolationMethod::XYZD65 { });
     default:
-        return std::nullopt;
+        return { };
     }
 }
 
@@ -2345,8 +2384,11 @@
 {
     ColorMixComponent result;
 
-    if (auto percentage = consumePercentRaw(args))
+    if (auto percentage = consumePercentRaw(args)) {
+        if (*percentage < 0.0 || *percentage > 100.0)
+            return { };
         result.percentage = percentage;
+    }
 
     result.color = consumeOriginColor(args, context);
     if (!result.color.isValid())
@@ -2353,8 +2395,11 @@
         return std::nullopt;
 
     if (!result.percentage) {
-        if (auto percentage = consumePercentRaw(args))
+        if (auto percentage = consumePercentRaw(args)) {
+            if (*percentage < 0.0 || *percentage > 100.0)
+                return { };
             result.percentage = percentage;
+        }
     }
 
     return result;
@@ -2363,9 +2408,10 @@
 struct ColorMixPercentages {
     double p1;
     double p2;
+    std::optional<double> alphaMultiplier = std::nullopt;
 };
 
-static ColorMixPercentages normalizedMixPercentages(const ColorMixComponent& mixComponents1, const ColorMixComponent& mixComponents2)
+static std::optional<ColorMixPercentages> normalizedMixPercentages(const ColorMixComponent& mixComponents1, const ColorMixComponent& mixComponents2)
 {
     // The percentages are normalized as follows:
 
@@ -2373,7 +2419,7 @@
 
     // 2. If both percentages are omitted, they each default to 50% (an equal mix of the two colors).
     if (!mixComponents1.percentage && !mixComponents2.percentage)
-        return { 50.0, 50.0 };
+        return {{ 50.0, 50.0 }};
     
     ColorMixPercentages result;
 
@@ -2392,22 +2438,29 @@
 
     auto sum = result.p1 + result.p2;
 
-    // 5. If the percentages sum to zero do something, tbd. (FIXME: We just use 50 / 50 for this case for now).
+    // 5.If the percentages sum to zero, the function is invalid.
     if (sum == 0)
-        return { 50.0, 50.0 };
+        return { };
 
-    if (sum != 100.0) {
+    if (sum > 100.0) {
         // 6. Otherwise, if both are provided but do not add up to 100%, they are scaled accordingly so that they
-        //    add up to 100%. This means that p1 becomes p1 / (p1 + p2) and p2 becomes p2 / (p1 + p2).
+        //    add up to 100%.
         result.p1 *= 100.0 / sum;
         result.p2 *= 100.0 / sum;
+    } else if (sum < 100.0) {
+        // 7. Otherwise, if both are provided and add up to less than 100%, the sum is saved as an alpha multiplier.
+        //    They are then scaled accordingly so that they add up to 100%.
+        result.p1 *= 100.0 / sum;
+        result.p2 *= 100.0 / sum;
+        result.alphaMultiplier = sum;
     }
 
     return result;
 }
 
-// Normalization is special cased for HWBA, which needs to normalize the whiteness and blackness components and convert to sRGB
-// and HSLA, which just needs to be converted to sRGB. All other color types can go through this non-specialized case.
+// Normalization is special cased for for all polar color spaces to renormalize the hue, with additional normalization needed
+// for HWBA to normalize the whiteness and blackness components. Furthermore, HWBA and HSLA also get converted to SRGBA which
+// is their canonical form.
 
 template<typename ColorType> inline Color makeColorTypeByNormalizingComponentsAfterMix(const ColorComponents<float, 4>& colorComponents)
 {
@@ -2418,84 +2471,76 @@
 {
     auto [hue, whiteness, blackness, alpha] = colorComponents;
     auto [normalizedWhitness, normalizedBlackness] = normalizeWhitenessBlackness(whiteness, blackness);
+    float normalizedHue = normalizeHue(hue);
 
-    return convertColor<SRGBA<uint8_t>>(HWBA<float> { hue, normalizedWhitness, normalizedBlackness, alpha });
+    return convertColor<SRGBA<uint8_t>>(HWBA<float> { normalizedHue, normalizedWhitness, normalizedBlackness, alpha });
 }
 
 template<> inline Color makeColorTypeByNormalizingComponentsAfterMix<HSLA<float>>(const ColorComponents<float, 4>& colorComponents)
 {
-    return convertColor<SRGBA<uint8_t>>(makeFromComponents<HSLA<float>>(colorComponents));
+    auto [hue, saturation, lightness, alpha] = colorComponents;
+    float normalizedHue = normalizeHue(hue);
+
+    return convertColor<SRGBA<uint8_t>>(HSLA<float> { normalizedHue, saturation, lightness, alpha });
 }
 
-template<size_t I, typename ComponentType> static void fixupHueComponentsPriorToMix(ColorComponents<ComponentType, 4>& colorComponents1, ColorComponents<ComponentType, 4>& colorComponents2)
+template<> inline Color makeColorTypeByNormalizingComponentsAfterMix<LCHA<float>>(const ColorComponents<float, 4>& colorComponents)
 {
-    auto normalizeAnglesUsingShorterAlgorithm = [] (auto theta1, auto theta2) -> std::pair<ComponentType, ComponentType> {
-        // https://drafts.csswg.org/css-color-4/#hue-shorter
-        auto difference = theta2 - theta1;
-        if (difference > 180.0)
-            return { theta1 + 360.0, theta2 };
-        if (difference < -180.0)
-            return { theta1, theta2 + 360.0 };
-        return { theta1, theta2 };
-    };
+    auto [lightness, chroma, hue, alpha] = colorComponents;
+    float normalizedHue = normalizeHue(hue);
 
-    // As no other interpolation type was specified, all angles should be normalized to use the "shorter" algorithm.
-    auto [theta1, theta2] = normalizeAnglesUsingShorterAlgorithm(colorComponents1[I], colorComponents2[I]);
-    colorComponents1[I] = theta1;
-    colorComponents2[I] = theta2;
+    return LCHA<float> { lightness, chroma, normalizedHue, alpha };
 }
 
-template<typename ColorType> static Color mixColorComponentsInColorSpace(ColorMixPercentages mixPercentages, const Color& color1, const Color& color2)
+template<> inline Color makeColorTypeByNormalizingComponentsAfterMix<OKLCHA<float>>(const ColorComponents<float, 4>& colorComponents)
 {
+    auto [lightness, chroma, hue, alpha] = colorComponents;
+    float normalizedHue = normalizeHue(hue);
+
+    return OKLCHA<float> { lightness, chroma, normalizedHue, alpha };
+}
+
+template<typename Method> static Color mixColorComponentsUsingColorInterpolationMethod(Method colorInterpolationMethod, ColorMixPercentages mixPercentages, const Color& color1, const Color& color2)
+{
+    using ColorType = typename Method::ColorType;
+
+    // 1. Both colors are converted to the specified <color-space>. If the specified color space has a smaller gamut than
+    //    the one in which the color to be adjusted is specified, gamut mapping will occur.
     auto colorComponents1 = asColorComponents(color1.template toColorTypeLossy<ColorType>());
     auto colorComponents2 = asColorComponents(color2.template toColorTypeLossy<ColorType>());
 
-    // Perform fixups on any hue/angle components.
-    constexpr auto componentInfo = ColorType::Model::componentInfo;
-    if constexpr (componentInfo[0].type == ColorComponentType::Angle)
-        fixupHueComponentsPriorToMix<0>(colorComponents1, colorComponents2);
-    if constexpr (componentInfo[1].type == ColorComponentType::Angle)
-        fixupHueComponentsPriorToMix<1>(colorComponents1, colorComponents2);
-    if constexpr (componentInfo[2].type == ColorComponentType::Angle)
-        fixupHueComponentsPriorToMix<2>(colorComponents1, colorComponents2);
+    // 2. Colors are then interpolated in the specified color space, as described in CSS Color 4 § 13 Interpolation. [...]
+    auto [normalizedColorComponents1, normalizedColorComponents2] = preInterpolationNormalization(colorInterpolationMethod, colorComponents1, colorComponents2);
 
-    auto colorComponentsMixed = mapColorComponents([&] (auto componentFromColor1, auto componentFromColor2) -> float {
+    auto mixedColorComponents = mapColorComponents([&] (auto componentFromColor1, auto componentFromColor2) -> float {
         return (componentFromColor1 * mixPercentages.p1 / 100.0) + (componentFromColor2 * mixPercentages.p2 / 100.0);
-    }, colorComponents1, colorComponents2);
+    }, normalizedColorComponents1, normalizedColorComponents2);
 
-    return makeColorTypeByNormalizingComponentsAfterMix<ColorType>(colorComponentsMixed);
+    // 3. If an alpha multiplier was produced during percentage normalization, the alpha component of the interpolated result
+    //    is multiplied by the alpha multiplier.
+    if (mixPercentages.alphaMultiplier)
+        mixedColorComponents[3] *= (*mixPercentages.alphaMultiplier / 100.0);
+
+    return makeColorTypeByNormalizingComponentsAfterMix<ColorType>(mixedColorComponents);
 }
 
-static Color mixColorComponents(ColorMixColorSpace colorSpace, const ColorMixComponent& mixComponents1, const ColorMixComponent& mixComponents2)
+static Color mixColorComponents(ColorInterpolationMethod colorInterpolationMethod, const ColorMixComponent& mixComponents1, const ColorMixComponent& mixComponents2)
 {
     auto mixPercentages = normalizedMixPercentages(mixComponents1, mixComponents2);
+    if (!mixPercentages)
+        return { };
 
-    switch (colorSpace) {
-    case ColorMixColorSpace::Hsl:
-        return mixColorComponentsInColorSpace<HSLA<float>>(mixPercentages, mixComponents1.color, mixComponents2.color);
-    case ColorMixColorSpace::Hwb:
-        return mixColorComponentsInColorSpace<HWBA<float>>(mixPercentages, mixComponents1.color, mixComponents2.color);
-    case ColorMixColorSpace::Lab:
-        return mixColorComponentsInColorSpace<Lab<float>>(mixPercentages, mixComponents1.color, mixComponents2.color);
-    case ColorMixColorSpace::Lch:
-        return mixColorComponentsInColorSpace<LCHA<float>>(mixPercentages, mixComponents1.color, mixComponents2.color);
-    case ColorMixColorSpace::Oklab:
-        return mixColorComponentsInColorSpace<OKLab<float>>(mixPercentages, mixComponents1.color, mixComponents2.color);
-    case ColorMixColorSpace::Oklch:
-        return mixColorComponentsInColorSpace<OKLCHA<float>>(mixPercentages, mixComponents1.color, mixComponents2.color);
-    case ColorMixColorSpace::Srgb:
-        return mixColorComponentsInColorSpace<SRGBA<float>>(mixPercentages, mixComponents1.color, mixComponents2.color);
-    case ColorMixColorSpace::XyzD50:
-        return mixColorComponentsInColorSpace<XYZA<float, WhitePoint::D50>>(mixPercentages, mixComponents1.color, mixComponents2.color);
-    case ColorMixColorSpace::XyzD65:
-        return mixColorComponentsInColorSpace<XYZA<float, WhitePoint::D65>>(mixPercentages, mixComponents1.color, mixComponents2.color);
-    }
-
-    RELEASE_ASSERT_NOT_REACHED();
+    return WTF::switchOn(colorInterpolationMethod.value,
+        [&] (auto method) {
+            return mixColorComponentsUsingColorInterpolationMethod<decltype(method)>(method, *mixPercentages, mixComponents1.color, mixComponents2.color);
+        }
+    );
 }
 
 static Color parseColorMixFunctionParameters(CSSParserTokenRange& range, const CSSParserContext& context)
 {
+    // color-mix() = color-mix( <color-interpolation-method> , [ <color> && <percentage [0,100]>? ]#{2})
+
     ASSERT(range.peek().functionId() == CSSValueColorMix);
 
     if (!context.colorMixEnabled)
@@ -2502,14 +2547,14 @@
         return { };
 
     auto args = consumeFunction(range);
-
-    if (!consumeIdentRaw<CSSValueIn>(args))
-        return { };
     
-    auto colorSpace = consumeColorMixColorSpaceAndComma(args);
-    if (!colorSpace)
+    auto colorInterpolationMethod = consumeColorInterpolationMethod(args);
+    if (!colorInterpolationMethod)
         return { };
 
+    if (!consumeCommaIncludingWhitespace(args))
+        return { };
+
     auto mixComponent1 = consumeColorMixComponent(args, context);
     if (!mixComponent1)
         return { };
@@ -2524,7 +2569,7 @@
     if (!args.atEnd())
         return { };
 
-    return mixColorComponents(*colorSpace, *mixComponent1, *mixComponent2);
+    return mixColorComponents(*colorInterpolationMethod, *mixComponent1, *mixComponent2);
 }
 
 static std::optional<SRGBA<uint8_t>> parseHexColor(CSSParserTokenRange& range, bool acceptQuirkyColors)

Added: trunk/Source/WebCore/platform/graphics/ColorInterpolation.cpp (0 => 286196)


--- trunk/Source/WebCore/platform/graphics/ColorInterpolation.cpp	                        (rev 0)
+++ trunk/Source/WebCore/platform/graphics/ColorInterpolation.cpp	2021-11-28 18:55:05 UTC (rev 286196)
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2021 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1.  Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ * 2.  Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include "config.h"
+#include "ColorInterpolation.h"
+
+namespace WebCore {
+
+std::pair<float, float> fixupHueComponentsPriorToInterpolation(HueInterpolationMethod method, float component1, float component2)
+{
+    auto normalizeAnglesUsingShorterAlgorithm = [] (auto theta1, auto theta2) -> std::pair<float, float> {
+        // https://drafts.csswg.org/css-color-4/#hue-shorter
+        auto difference = theta2 - theta1;
+        if (difference > 180.0)
+            return { theta1 + 360.0, theta2 };
+        if (difference < -180.0)
+            return { theta1, theta2 + 360.0 };
+        return { theta1, theta2 };
+    };
+
+    auto normalizeAnglesUsingLongerAlgorithm = [] (auto theta1, auto theta2) -> std::pair<float, float> {
+        // https://drafts.csswg.org/css-color-4/#hue-longer
+        auto difference = theta2 - theta1;
+        if (difference > 0.0 && difference < 180.0)
+            return { theta1 + 360.0, theta2 };
+        if (difference > -180.0 && difference < 0)
+            return { theta1, theta2 + 360.0 };
+        return { theta1, theta2 };
+    };
+
+    auto normalizeAnglesUsingIncreasingAlgorithm = [] (auto theta1, auto theta2) -> std::pair<float, float> {
+        // https://drafts.csswg.org/css-color-4/#hue-increasing
+        if (theta2 < theta1)
+            return { theta1, theta2 + 360.0 };
+        return { theta1, theta2 };
+    };
+
+    auto normalizeAnglesUsingDecreasingAlgorithm = [] (auto theta1, auto theta2) -> std::pair<float, float> {
+        // https://drafts.csswg.org/css-color-4/#hue-decreasing
+        if (theta1 < theta2)
+            return { theta1 + 360.0, theta2 };
+        return { theta1, theta2 };
+    };
+
+    auto normalizeAnglesUsingSpecifiedAlgorithm = [] (auto theta1, auto theta2) -> std::pair<float, float> {
+        // https://drafts.csswg.org/css-color-4/#hue-specified
+        return { theta1, theta2 };
+    };
+
+    switch (method) {
+    case HueInterpolationMethod::Shorter:
+        return normalizeAnglesUsingShorterAlgorithm(component1, component2);
+    case HueInterpolationMethod::Longer:
+        return normalizeAnglesUsingLongerAlgorithm(component1, component2);
+    case HueInterpolationMethod::Increasing:
+        return normalizeAnglesUsingIncreasingAlgorithm(component1, component2);
+    case HueInterpolationMethod::Decreasing:
+        return normalizeAnglesUsingDecreasingAlgorithm(component1, component2);
+    case HueInterpolationMethod::Specified:
+        return normalizeAnglesUsingSpecifiedAlgorithm(component1, component2);
+    }
+}
+
+}

Added: trunk/Source/WebCore/platform/graphics/ColorInterpolation.h (0 => 286196)


--- trunk/Source/WebCore/platform/graphics/ColorInterpolation.h	                        (rev 0)
+++ trunk/Source/WebCore/platform/graphics/ColorInterpolation.h	2021-11-28 18:55:05 UTC (rev 286196)
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2021 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1.  Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ * 2.  Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include "ColorTypes.h"
+#include <variant>
+
+namespace WebCore {
+
+enum class HueInterpolationMethod : uint8_t {
+    Shorter,
+    Longer,
+    Increasing,
+    Decreasing,
+    Specified
+};
+
+struct ColorInterpolationMethod {
+    struct HSL {
+        using ColorType = WebCore::HSLA<float>;
+        HueInterpolationMethod hueInterpolationMethod = HueInterpolationMethod::Shorter;
+    };
+    struct HWB {
+        using ColorType = WebCore::HWBA<float>;
+        HueInterpolationMethod hueInterpolationMethod = HueInterpolationMethod::Shorter;
+    };
+    struct LCH {
+        using ColorType = WebCore::LCHA<float>;
+        HueInterpolationMethod hueInterpolationMethod = HueInterpolationMethod::Shorter;
+    };
+    struct Lab {
+        using ColorType = WebCore::Lab<float>;
+    };
+    struct OKLCH {
+        using ColorType = WebCore::OKLCHA<float>;
+        HueInterpolationMethod hueInterpolationMethod = HueInterpolationMethod::Shorter;
+    };
+    struct OKLab {
+        using ColorType = WebCore::OKLab<float>;
+    };
+    struct SRGB {
+        using ColorType = WebCore::SRGBA<float>;
+    };
+    struct XYZD50 {
+        using ColorType = WebCore::XYZA<float, WhitePoint::D50>;
+    };
+    struct XYZD65 {
+        using ColorType = WebCore::XYZA<float, WhitePoint::D65>;
+    };
+
+    std::variant<HSL, HWB, LCH, Lab, OKLCH, OKLab, SRGB, XYZD50, XYZD65> value;
+};
+
+std::pair<float, float> fixupHueComponentsPriorToInterpolation(HueInterpolationMethod, float, float);
+
+template<size_t I, typename InterpolationMethod>
+std::pair<float, float> preInterpolationNormalizationForComponent(InterpolationMethod interpolationMethod, ColorComponents<float, 4> colorComponents1, ColorComponents<float, 4> colorComponents2)
+{
+    using ColorType = typename InterpolationMethod::ColorType;
+    constexpr auto componentInfo = ColorType::Model::componentInfo;
+
+    if constexpr (componentInfo[I].type == ColorComponentType::Angle)
+        return fixupHueComponentsPriorToInterpolation(interpolationMethod.hueInterpolationMethod, colorComponents1[I], colorComponents2[I]);
+    else
+        return { colorComponents1[I], colorComponents2[I] };
+}
+
+template<typename InterpolationMethod>
+std::pair<ColorComponents<float, 4>, ColorComponents<float, 4>> preInterpolationNormalization(InterpolationMethod interpolationMethod, ColorComponents<float, 4> colorComponents1, ColorComponents<float, 4> colorComponents2)
+{
+    auto [colorA0, colorB0] = preInterpolationNormalizationForComponent<0>(interpolationMethod, colorComponents1, colorComponents2);
+    auto [colorA1, colorB1] = preInterpolationNormalizationForComponent<1>(interpolationMethod, colorComponents1, colorComponents2);
+    auto [colorA2, colorB2] = preInterpolationNormalizationForComponent<2>(interpolationMethod, colorComponents1, colorComponents2);
+
+    return {
+        ColorComponents<float, 4> { colorA0, colorA1, colorA2, colorComponents1[3] },
+        ColorComponents<float, 4> { colorB0, colorB1, colorB2, colorComponents2[3] }
+    };
+}
+
+}
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to