Parse color() function
authordino@apple.com <dino@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 29 Oct 2016 22:34:43 +0000 (22:34 +0000)
committerdino@apple.com <dino@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 29 Oct 2016 22:34:43 +0000 (22:34 +0000)
https://bugs.webkit.org/show_bug.cgi?id=164146
<rdar://problems/29007218>

Reviewed by Darin Adler.

Source/WebCore:

Support the new CSS color() function:
https://drafts.csswg.org/css-color/#color-function

There are separate code paths for the old and new CSS parser.

Tests: css3/color/color-function-computed-style.html
       css3/color/color-function-parsing.html

* css/CSSComputedStyleDeclaration.cpp: Use Color directly, not via rgb().
(WebCore::ComputedStyleExtractor::currentColorOrValidColor):
(WebCore::ComputedStyleExtractor::valueForShadow):
(WebCore::ComputedStyleExtractor::propertyValue):

* css/CSSValueKeywords.in: Note that there is a color function, but the
keyword is already defined. Also add keywords for the color spaces.
* css/SVGCSSValueKeywords.in: sRGB is used outside of SVG now.

* css/parser/CSSParser.cpp: Old CSS parser code to handle color().
(WebCore::isPercent): Helper to tell if a ValueWithCalculation is a percentage or not.
(WebCore::CSSParser::parseColorInt): Renamed.
(WebCore::CSSParser::parseColorDouble): Helper to get a Number/Percentage into a double
(WebCore::CSSParser::parseRGBParameters):
(WebCore::CSSParser::parseColorFunctionParameters):
(WebCore::CSSParser::parseHSLParameters):
(WebCore::CSSParser::parseColorFromValue):
(WebCore::CSSParser::colorIntFromValue): Deleted.
* css/parser/CSSParser.h:

* css/parser/CSSPropertyParserHelpers.cpp: New CSS parser code to handle color().
(WebCore::CSSPropertyParserHelpers::parseColorFunctionParameters):
(WebCore::CSSPropertyParserHelpers::parseColorFunction):

* platform/graphics/Color.h:
(WebCore::Color::isValid): An extended color is valid.
(WebCore::Color::rgb): Move the code to a standalone inline
so that I could add a longer comment.

* platform/graphics/ExtendedColor.cpp:
(WebCore::ExtendedColor::cssText): Alpha output is only needed if != 1.

* platform/graphics/cg/ColorCG.cpp: Handle ExtendedColor -> CGColor.
(WebCore::leakCGColor):
(WebCore::cachedCGColor):

LayoutTests:

Test that exercises the new color() function in CSS. It checks
all valid and invalid input, with the exception of fallback content.

* css3/color/color-function-computed-style-expected.txt: Added.
* css3/color/color-function-computed-style.html: Added.
* css3/color/color-function-parsing-expected.txt: Added.
* css3/color/color-function-parsing.html: Added.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@208116 268f45cc-cd09-0410-ab3c-d52691b4dbfc

15 files changed:
LayoutTests/ChangeLog
LayoutTests/css3/color/color-function-computed-style-expected.txt [new file with mode: 0644]
LayoutTests/css3/color/color-function-computed-style.html [new file with mode: 0644]
LayoutTests/css3/color/color-function-parsing-expected.txt [new file with mode: 0644]
LayoutTests/css3/color/color-function-parsing.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/css/CSSComputedStyleDeclaration.cpp
Source/WebCore/css/CSSValueKeywords.in
Source/WebCore/css/SVGCSSValueKeywords.in
Source/WebCore/css/parser/CSSParser.cpp
Source/WebCore/css/parser/CSSParser.h
Source/WebCore/css/parser/CSSPropertyParserHelpers.cpp
Source/WebCore/platform/graphics/Color.h
Source/WebCore/platform/graphics/ExtendedColor.cpp
Source/WebCore/platform/graphics/cg/ColorCG.cpp

index bd44bfa..c90c0a3 100644 (file)
@@ -1,3 +1,19 @@
+2016-10-29  Dean Jackson  <dino@apple.com>
+
+        Parse color() function
+        https://bugs.webkit.org/show_bug.cgi?id=164146
+        <rdar://problems/29007218>
+
+        Reviewed by Darin Adler.
+
+        Test that exercises the new color() function in CSS. It checks
+        all valid and invalid input, with the exception of fallback content.
+
+        * css3/color/color-function-computed-style-expected.txt: Added.
+        * css3/color/color-function-computed-style.html: Added.
+        * css3/color/color-function-parsing-expected.txt: Added.
+        * css3/color/color-function-parsing.html: Added.
+
 2016-10-29  Youenn Fablet  <youenn@apple.com>
 
         Remove testharness.js/testharnessreport.js unnecessary copies
diff --git a/LayoutTests/css3/color/color-function-computed-style-expected.txt b/LayoutTests/css3/color/color-function-computed-style-expected.txt
new file mode 100644 (file)
index 0000000..1594a77
--- /dev/null
@@ -0,0 +1,112 @@
+Test the computed style of the color() function.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+
+TEST: Basic sRGB white --> color(srgb 1 1 1)
+PASS declaration.getPropertyValue('color') is 'color(srgb 1 1 1)'
+
+TEST: White with lots of space --> color(    srgb         1      1 1       )
+PASS declaration.getPropertyValue('color') is 'color(srgb 1 1 1)'
+
+TEST: sRGB color --> color(srgb 0.25 0.5 0.75)
+PASS declaration.getPropertyValue('color') is 'color(srgb 0.25 0.5 0.75)'
+
+TEST: Different case for sRGB --> color(SrGb 0.25 0.5 0.75)
+PASS declaration.getPropertyValue('color') is 'color(srgb 0.25 0.5 0.75)'
+
+TEST: sRGB color with unnecessary decimals --> color(srgb 1.00000 0.500000 0.20)
+PASS declaration.getPropertyValue('color') is 'color(srgb 1 0.5 0.2)'
+
+TEST: sRGB white with 0.5 alpha --> color(srgb 1 1 1 / 0.5)
+PASS declaration.getPropertyValue('color') is 'color(srgb 1 1 1 / 0.5)'
+
+TEST: sRGB white with 0 alpha --> color(srgb 1 1 1 / 0)
+PASS declaration.getPropertyValue('color') is 'color(srgb 1 1 1 / 0)'
+
+TEST: sRGB white with 50% alpha --> color(srgb 1 1 1 / 50%)
+PASS declaration.getPropertyValue('color') is 'color(srgb 1 1 1 / 0.5)'
+
+TEST: sRGB white with 0% alpha --> color(srgb 1 1 1 / 0%)
+PASS declaration.getPropertyValue('color') is 'color(srgb 1 1 1 / 0)'
+
+TEST: One missing component is 0 --> color(srgb 1 1)
+PASS declaration.getPropertyValue('color') is 'color(srgb 1 1 0)'
+
+TEST: Two missing components are 0 --> color(srgb 1)
+PASS declaration.getPropertyValue('color') is 'color(srgb 1 0 0)'
+
+TEST: All components missing --> color(srgb)
+PASS declaration.getPropertyValue('color') is 'color(srgb 0 0 0)'
+
+TEST: Display P3 color --> color(display-p3 0.6 0.7 0.8)
+PASS declaration.getPropertyValue('color') is 'color(display-p3 0.6 0.7 0.8)'
+
+TEST: Different case for Display P3 --> color(dIspLaY-P3 0.6 0.7 0.8)
+PASS declaration.getPropertyValue('color') is 'color(display-p3 0.6 0.7 0.8)'
+
+
+Fallback tests.
+
+
+TEST: Unknown color space should fallback --> color(unknown 1 2 3, red)
+FAIL declaration.getPropertyValue('color') should be color(unknown 1 2 3, red). Was rgb(0, 0, 0).
+
+
+Clamping tests.
+
+
+TEST: sRGB color with negative component should clamp to 0 --> color(srgb -0.25 0.5 0.75)
+PASS declaration.getPropertyValue('color') is 'color(srgb 0 0.5 0.75)'
+
+TEST: sRGB color with component > 1 should clamp --> color(srgb 0.25 1.5 0.75)
+PASS declaration.getPropertyValue('color') is 'color(srgb 0.25 1 0.75)'
+
+TEST: Display P3 color with negative component should clamp to 0 --> color(display-p3 0.5 -199 0.75)
+PASS declaration.getPropertyValue('color') is 'color(display-p3 0.5 0 0.75)'
+
+TEST: Display P3 color with component > 1 should clamp --> color(display-p3 184 1.00001 2347329746587)
+PASS declaration.getPropertyValue('color') is 'color(display-p3 1 1 1)'
+
+TEST: Alpha > 1 should clamp --> color(srgb 0.1 0.2 0.3 / 1.9)
+PASS declaration.getPropertyValue('color') is 'color(srgb 0.1 0.2 0.3)'
+
+TEST: Negative alpha should clamp --> color(srgb 1 1 1 / -0.2)
+PASS declaration.getPropertyValue('color') is 'color(srgb 1 1 1 / 0)'
+
+
+Invalid property value tests.
+
+
+TEST: Empty --> color()
+PASS declaration.getPropertyValue('color') is 'rgb(0, 0, 0)'
+
+TEST: Bad color space --> color(banana 1 1 1)
+PASS declaration.getPropertyValue('color') is 'rgb(0, 0, 0)'
+
+TEST: Bad Display P3 color space --> color(displayp3 1 1 1)
+PASS declaration.getPropertyValue('color') is 'rgb(0, 0, 0)'
+
+TEST: No color space --> color(1 1 1)
+PASS declaration.getPropertyValue('color') is 'rgb(0, 0, 0)'
+
+TEST: Too many parameters --> color(srgb 1 1 1 1)
+PASS declaration.getPropertyValue('color') is 'rgb(0, 0, 0)'
+
+TEST: Way too many parameters --> color(srgb 1 1 1 1 1)
+PASS declaration.getPropertyValue('color') is 'rgb(0, 0, 0)'
+
+TEST: Bad parameters --> color(srgb 1 eggs 1)
+PASS declaration.getPropertyValue('color') is 'rgb(0, 0, 0)'
+
+TEST: Bad alpha --> color(srgb 1 1 1 / bacon)
+PASS declaration.getPropertyValue('color') is 'rgb(0, 0, 0)'
+
+TEST: Junk after alpha --> color(srgb 1 1 1 / 1 cucumber)
+PASS declaration.getPropertyValue('color') is 'rgb(0, 0, 0)'
+
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/css3/color/color-function-computed-style.html b/LayoutTests/css3/color/color-function-computed-style.html
new file mode 100644 (file)
index 0000000..22fbe9f
--- /dev/null
@@ -0,0 +1,85 @@
+<style></style>
+<script src="../../resources/js-test-pre.js"></script>
+<p id="description"></p>
+<div id="console"></div>
+<p id="target"></p>
+<script>
+description("Test the computed style of the color() function.");
+
+let styleElement = document.querySelector("style");
+let stylesheet = styleElement.sheet;
+let cssRule, declaration;
+
+function testColorFunction(description, rule, expectedValue)
+{
+    debug("");
+    debug(`TEST: ${description} --> ${rule}`);
+
+    stylesheet.insertRule(`#target { color: ${rule}; }`, 0);
+    cssRule = stylesheet.cssRules.item(0);
+
+    declaration = window.getComputedStyle(document.getElementById("target"));
+    shouldBe("declaration.getPropertyValue('color')", `'${expectedValue}'`);
+    stylesheet.deleteRule(0);
+}
+
+testColorFunction("Basic sRGB white", "color(srgb 1 1 1)", "color(srgb 1 1 1)");
+testColorFunction("White with lots of space", "color(    srgb         1      1 1       )", "color(srgb 1 1 1)");
+testColorFunction("sRGB color", "color(srgb 0.25 0.5 0.75)", "color(srgb 0.25 0.5 0.75)");
+testColorFunction("Different case for sRGB", "color(SrGb 0.25 0.5 0.75)", "color(srgb 0.25 0.5 0.75)");
+testColorFunction("sRGB color with unnecessary decimals", "color(srgb 1.00000 0.500000 0.20)", "color(srgb 1 0.5 0.2)");
+
+testColorFunction("sRGB white with 0.5 alpha", "color(srgb 1 1 1 / 0.5)", "color(srgb 1 1 1 / 0.5)");
+testColorFunction("sRGB white with 0 alpha", "color(srgb 1 1 1 / 0)", "color(srgb 1 1 1 / 0)");
+testColorFunction("sRGB white with 50% alpha", "color(srgb 1 1 1 / 50%)", "color(srgb 1 1 1 / 0.5)");
+testColorFunction("sRGB white with 0% alpha", "color(srgb 1 1 1 / 0%)", "color(srgb 1 1 1 / 0)");
+
+testColorFunction("One missing component is 0", "color(srgb 1 1)", "color(srgb 1 1 0)");
+testColorFunction("Two missing components are 0", "color(srgb 1)", "color(srgb 1 0 0)");
+testColorFunction("All components missing", "color(srgb)", "color(srgb 0 0 0)");
+
+testColorFunction("Display P3 color", "color(display-p3 0.6 0.7 0.8)", "color(display-p3 0.6 0.7 0.8)");
+testColorFunction("Different case for Display P3", "color(dIspLaY-P3 0.6 0.7 0.8)", "color(display-p3 0.6 0.7 0.8)");
+
+debug("");
+debug("");
+debug("Fallback tests.")
+debug("");
+
+testColorFunction("Unknown color space should fallback", "color(unknown 1 2 3, red)", "color(unknown 1 2 3, red)");
+
+debug("");
+debug("");
+debug("Clamping tests.")
+debug("");
+
+// NOTE: If we start supporting extended-range sRGB without changing the keyword, these
+// tests may fail.
+testColorFunction("sRGB color with negative component should clamp to 0", "color(srgb -0.25 0.5 0.75)", "color(srgb 0 0.5 0.75)");
+testColorFunction("sRGB color with component > 1 should clamp", "color(srgb 0.25 1.5 0.75)", "color(srgb 0.25 1 0.75)");
+
+testColorFunction("Display P3 color with negative component should clamp to 0", "color(display-p3 0.5 -199 0.75)", "color(display-p3 0.5 0 0.75)");
+testColorFunction("Display P3 color with component > 1 should clamp", "color(display-p3 184 1.00001 2347329746587)", "color(display-p3 1 1 1)");
+
+testColorFunction("Alpha > 1 should clamp", "color(srgb 0.1 0.2 0.3 / 1.9)", "color(srgb 0.1 0.2 0.3)");
+testColorFunction("Negative alpha should clamp", "color(srgb 1 1 1 / -0.2)", "color(srgb 1 1 1 / 0)");
+
+debug("");
+debug("");
+debug("Invalid property value tests.")
+debug("");
+
+testColorFunction("Empty", "color()", "rgb(0, 0, 0)");
+testColorFunction("Bad color space", "color(banana 1 1 1)", "rgb(0, 0, 0)");
+testColorFunction("Bad Display P3 color space", "color(displayp3 1 1 1)", "rgb(0, 0, 0)");
+testColorFunction("No color space", "color(1 1 1)", "rgb(0, 0, 0)");
+testColorFunction("Too many parameters", "color(srgb 1 1 1 1)", "rgb(0, 0, 0)");
+testColorFunction("Way too many parameters", "color(srgb 1 1 1 1 1)", "rgb(0, 0, 0)");
+testColorFunction("Bad parameters", "color(srgb 1 eggs 1)", "rgb(0, 0, 0)");
+testColorFunction("Bad alpha", "color(srgb 1 1 1 / bacon)", "rgb(0, 0, 0)");
+testColorFunction("Junk after alpha", "color(srgb 1 1 1 / 1 cucumber)", "rgb(0, 0, 0)");
+
+debug("");
+
+</script>
+<script src="../../resources/js-test-post.js"></script>
diff --git a/LayoutTests/css3/color/color-function-parsing-expected.txt b/LayoutTests/css3/color/color-function-parsing-expected.txt
new file mode 100644 (file)
index 0000000..1b3355d
--- /dev/null
@@ -0,0 +1,112 @@
+Test the parsing of the color() function.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+
+TEST: Basic sRGB white --> color(srgb 1 1 1)
+PASS declaration.getPropertyValue('color') is 'color(srgb 1 1 1)'
+
+TEST: White with lots of space --> color(    srgb         1      1 1       )
+PASS declaration.getPropertyValue('color') is 'color(srgb 1 1 1)'
+
+TEST: sRGB color --> color(srgb 0.25 0.5 0.75)
+PASS declaration.getPropertyValue('color') is 'color(srgb 0.25 0.5 0.75)'
+
+TEST: Different case for sRGB --> color(SrGb 0.25 0.5 0.75)
+PASS declaration.getPropertyValue('color') is 'color(srgb 0.25 0.5 0.75)'
+
+TEST: sRGB color with unnecessary decimals --> color(srgb 1.00000 0.500000 0.20)
+PASS declaration.getPropertyValue('color') is 'color(srgb 1 0.5 0.2)'
+
+TEST: sRGB white with 0.5 alpha --> color(srgb 1 1 1 / 0.5)
+PASS declaration.getPropertyValue('color') is 'color(srgb 1 1 1 / 0.5)'
+
+TEST: sRGB white with 0 alpha --> color(srgb 1 1 1 / 0)
+PASS declaration.getPropertyValue('color') is 'color(srgb 1 1 1 / 0)'
+
+TEST: sRGB white with 50% alpha --> color(srgb 1 1 1 / 50%)
+PASS declaration.getPropertyValue('color') is 'color(srgb 1 1 1 / 0.5)'
+
+TEST: sRGB white with 0% alpha --> color(srgb 1 1 1 / 0%)
+PASS declaration.getPropertyValue('color') is 'color(srgb 1 1 1 / 0)'
+
+TEST: One missing component is 0 --> color(srgb 1 1)
+PASS declaration.getPropertyValue('color') is 'color(srgb 1 1 0)'
+
+TEST: Two missing components are 0 --> color(srgb 1)
+PASS declaration.getPropertyValue('color') is 'color(srgb 1 0 0)'
+
+TEST: All components missing --> color(srgb)
+PASS declaration.getPropertyValue('color') is 'color(srgb 0 0 0)'
+
+TEST: Display P3 color --> color(display-p3 0.6 0.7 0.8)
+PASS declaration.getPropertyValue('color') is 'color(display-p3 0.6 0.7 0.8)'
+
+TEST: Different case for Display P3 --> color(dIspLaY-P3 0.6 0.7 0.8)
+PASS declaration.getPropertyValue('color') is 'color(display-p3 0.6 0.7 0.8)'
+
+
+Fallback tests.
+
+
+TEST: Unknown color space should fallback --> color(unknown 1 2 3, red)
+FAIL declaration.getPropertyValue('color') should be color(unknown 1 2 3, red). Was .
+
+
+Clamping tests.
+
+
+TEST: sRGB color with negative component should clamp to 0 --> color(srgb -0.25 0.5 0.75)
+PASS declaration.getPropertyValue('color') is 'color(srgb 0 0.5 0.75)'
+
+TEST: sRGB color with component > 1 should clamp --> color(srgb 0.25 1.5 0.75)
+PASS declaration.getPropertyValue('color') is 'color(srgb 0.25 1 0.75)'
+
+TEST: Display P3 color with negative component should clamp to 0 --> color(display-p3 0.5 -199 0.75)
+PASS declaration.getPropertyValue('color') is 'color(display-p3 0.5 0 0.75)'
+
+TEST: Display P3 color with component > 1 should clamp --> color(display-p3 184 1.00001 2347329746587)
+PASS declaration.getPropertyValue('color') is 'color(display-p3 1 1 1)'
+
+TEST: Alpha > 1 should clamp --> color(srgb 0.1 0.2 0.3 / 1.9)
+PASS declaration.getPropertyValue('color') is 'color(srgb 0.1 0.2 0.3)'
+
+TEST: Negative alpha should clamp --> color(srgb 1 1 1 / -0.2)
+PASS declaration.getPropertyValue('color') is 'color(srgb 1 1 1 / 0)'
+
+
+Invalid property value tests.
+
+
+TEST: Empty --> color()
+PASS declaration.getPropertyValue('color') is ''
+
+TEST: Bad color space --> color(banana 1 1 1)
+PASS declaration.getPropertyValue('color') is ''
+
+TEST: Bad Display P3 color space --> color(displayp3 1 1 1)
+PASS declaration.getPropertyValue('color') is ''
+
+TEST: No color space --> color(1 1 1)
+PASS declaration.getPropertyValue('color') is ''
+
+TEST: Too many parameters --> color(srgb 1 1 1 1)
+PASS declaration.getPropertyValue('color') is ''
+
+TEST: Way too many parameters --> color(srgb 1 1 1 1 1)
+PASS declaration.getPropertyValue('color') is ''
+
+TEST: Bad parameters --> color(srgb 1 eggs 1)
+PASS declaration.getPropertyValue('color') is ''
+
+TEST: Bad alpha --> color(srgb 1 1 1 / bacon)
+PASS declaration.getPropertyValue('color') is ''
+
+TEST: Junk after alpha --> color(srgb 1 1 1 / 1 cucumber)
+PASS declaration.getPropertyValue('color') is ''
+
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/css3/color/color-function-parsing.html b/LayoutTests/css3/color/color-function-parsing.html
new file mode 100644 (file)
index 0000000..f551650
--- /dev/null
@@ -0,0 +1,84 @@
+<style></style>
+<script src="../../resources/js-test-pre.js"></script>
+<p id="description"></p>
+<div id="console"></div>
+<script>
+description("Test the parsing of the color() function.");
+
+let styleElement = document.querySelector("style");
+let stylesheet = styleElement.sheet;
+let cssRule, declaration;
+
+function testColorFunction(description, rule, expectedValue)
+{
+    debug("");
+    debug(`TEST: ${description} --> ${rule}`);
+
+    stylesheet.insertRule(`#target { color: ${rule}; }`, 0);
+    cssRule = stylesheet.cssRules.item(0);
+
+    declaration = cssRule.style;
+    shouldBe("declaration.getPropertyValue('color')", `'${expectedValue}'`);
+    stylesheet.deleteRule(0);
+}
+
+testColorFunction("Basic sRGB white", "color(srgb 1 1 1)", "color(srgb 1 1 1)");
+testColorFunction("White with lots of space", "color(    srgb         1      1 1       )", "color(srgb 1 1 1)");
+testColorFunction("sRGB color", "color(srgb 0.25 0.5 0.75)", "color(srgb 0.25 0.5 0.75)");
+testColorFunction("Different case for sRGB", "color(SrGb 0.25 0.5 0.75)", "color(srgb 0.25 0.5 0.75)");
+testColorFunction("sRGB color with unnecessary decimals", "color(srgb 1.00000 0.500000 0.20)", "color(srgb 1 0.5 0.2)");
+
+testColorFunction("sRGB white with 0.5 alpha", "color(srgb 1 1 1 / 0.5)", "color(srgb 1 1 1 / 0.5)");
+testColorFunction("sRGB white with 0 alpha", "color(srgb 1 1 1 / 0)", "color(srgb 1 1 1 / 0)");
+testColorFunction("sRGB white with 50% alpha", "color(srgb 1 1 1 / 50%)", "color(srgb 1 1 1 / 0.5)");
+testColorFunction("sRGB white with 0% alpha", "color(srgb 1 1 1 / 0%)", "color(srgb 1 1 1 / 0)");
+
+testColorFunction("One missing component is 0", "color(srgb 1 1)", "color(srgb 1 1 0)");
+testColorFunction("Two missing components are 0", "color(srgb 1)", "color(srgb 1 0 0)");
+testColorFunction("All components missing", "color(srgb)", "color(srgb 0 0 0)");
+
+testColorFunction("Display P3 color", "color(display-p3 0.6 0.7 0.8)", "color(display-p3 0.6 0.7 0.8)");
+testColorFunction("Different case for Display P3", "color(dIspLaY-P3 0.6 0.7 0.8)", "color(display-p3 0.6 0.7 0.8)");
+
+debug("");
+debug("");
+debug("Fallback tests.")
+debug("");
+
+testColorFunction("Unknown color space should fallback", "color(unknown 1 2 3, red)", "color(unknown 1 2 3, red)");
+
+debug("");
+debug("");
+debug("Clamping tests.")
+debug("");
+
+// NOTE: If we start supporting extended-range sRGB without changing the keyword, these
+// tests may fail.
+testColorFunction("sRGB color with negative component should clamp to 0", "color(srgb -0.25 0.5 0.75)", "color(srgb 0 0.5 0.75)");
+testColorFunction("sRGB color with component > 1 should clamp", "color(srgb 0.25 1.5 0.75)", "color(srgb 0.25 1 0.75)");
+
+testColorFunction("Display P3 color with negative component should clamp to 0", "color(display-p3 0.5 -199 0.75)", "color(display-p3 0.5 0 0.75)");
+testColorFunction("Display P3 color with component > 1 should clamp", "color(display-p3 184 1.00001 2347329746587)", "color(display-p3 1 1 1)");
+
+testColorFunction("Alpha > 1 should clamp", "color(srgb 0.1 0.2 0.3 / 1.9)", "color(srgb 0.1 0.2 0.3)");
+testColorFunction("Negative alpha should clamp", "color(srgb 1 1 1 / -0.2)", "color(srgb 1 1 1 / 0)");
+
+debug("");
+debug("");
+debug("Invalid property value tests.")
+debug("");
+
+testColorFunction("Empty", "color()", "");
+testColorFunction("Bad color space", "color(banana 1 1 1)", "");
+testColorFunction("Bad Display P3 color space", "color(displayp3 1 1 1)", "");
+testColorFunction("No color space", "color(1 1 1)", "");
+testColorFunction("Too many parameters", "color(srgb 1 1 1 1)", "");
+testColorFunction("Way too many parameters", "color(srgb 1 1 1 1 1)", "");
+testColorFunction("Bad parameters", "color(srgb 1 eggs 1)", "");
+testColorFunction("Bad alpha", "color(srgb 1 1 1 / bacon)", "");
+testColorFunction("Junk after alpha", "color(srgb 1 1 1 / 1 cucumber)", "");
+
+debug("");
+
+</script>
+<script src="../../resources/js-test-post.js"></script>
index 014ac36..fd24789 100644 (file)
@@ -1,3 +1,55 @@
+2016-10-29  Dean Jackson  <dino@apple.com>
+
+        Parse color() function
+        https://bugs.webkit.org/show_bug.cgi?id=164146
+        <rdar://problems/29007218>
+
+        Reviewed by Darin Adler.
+
+        Support the new CSS color() function:
+        https://drafts.csswg.org/css-color/#color-function
+
+        There are separate code paths for the old and new CSS parser.
+
+        Tests: css3/color/color-function-computed-style.html
+               css3/color/color-function-parsing.html
+
+        * css/CSSComputedStyleDeclaration.cpp: Use Color directly, not via rgb().
+        (WebCore::ComputedStyleExtractor::currentColorOrValidColor):
+        (WebCore::ComputedStyleExtractor::valueForShadow):
+        (WebCore::ComputedStyleExtractor::propertyValue):
+
+        * css/CSSValueKeywords.in: Note that there is a color function, but the
+        keyword is already defined. Also add keywords for the color spaces.
+        * css/SVGCSSValueKeywords.in: sRGB is used outside of SVG now.
+
+        * css/parser/CSSParser.cpp: Old CSS parser code to handle color().
+        (WebCore::isPercent): Helper to tell if a ValueWithCalculation is a percentage or not.
+        (WebCore::CSSParser::parseColorInt): Renamed.
+        (WebCore::CSSParser::parseColorDouble): Helper to get a Number/Percentage into a double
+        (WebCore::CSSParser::parseRGBParameters):
+        (WebCore::CSSParser::parseColorFunctionParameters):
+        (WebCore::CSSParser::parseHSLParameters):
+        (WebCore::CSSParser::parseColorFromValue):
+        (WebCore::CSSParser::colorIntFromValue): Deleted.
+        * css/parser/CSSParser.h:
+
+        * css/parser/CSSPropertyParserHelpers.cpp: New CSS parser code to handle color().
+        (WebCore::CSSPropertyParserHelpers::parseColorFunctionParameters):
+        (WebCore::CSSPropertyParserHelpers::parseColorFunction):
+
+        * platform/graphics/Color.h:
+        (WebCore::Color::isValid): An extended color is valid.
+        (WebCore::Color::rgb): Move the code to a standalone inline
+        so that I could add a longer comment.
+
+        * platform/graphics/ExtendedColor.cpp:
+        (WebCore::ExtendedColor::cssText): Alpha output is only needed if != 1.
+
+        * platform/graphics/cg/ColorCG.cpp: Handle ExtendedColor -> CGColor.
+        (WebCore::leakCGColor):
+        (WebCore::cachedCGColor):
+
 2016-10-29  Dave Hyatt  <hyatt@apple.com>
 
         [CSS Parser] Disable -webkit-text-size-adjust when the context says to.
index 6d99ce6..7bdbd91 100644 (file)
@@ -732,8 +732,8 @@ RefPtr<CSSPrimitiveValue> ComputedStyleExtractor::currentColorOrValidColor(const
 {
     // This function does NOT look at visited information, so that computed style doesn't expose that.
     if (!color.isValid())
-        return CSSValuePool::singleton().createColorValue(style->color().rgb());
-    return CSSValuePool::singleton().createColorValue(color.rgb());
+        return CSSValuePool::singleton().createColorValue(style->color());
+    return CSSValuePool::singleton().createColorValue(color);
 }
 
 static Ref<CSSPrimitiveValue> percentageOrZoomAdjustedValue(Length length, const RenderStyle& style)
@@ -901,7 +901,7 @@ Ref<CSSValue> ComputedStyleExtractor::valueForShadow(const ShadowData* shadow, C
         auto blur = adjustLengthForZoom(currShadowData->radius(), style, adjust);
         auto spread = propertyID == CSSPropertyTextShadow ? RefPtr<CSSPrimitiveValue>() : adjustLengthForZoom(currShadowData->spread(), style, adjust);
         auto style = propertyID == CSSPropertyTextShadow || currShadowData->style() == Normal ? RefPtr<CSSPrimitiveValue>() : cssValuePool.createIdentifierValue(CSSValueInset);
-        auto color = cssValuePool.createColorValue(currShadowData->color().rgb());
+        auto color = cssValuePool.createColorValue(currShadowData->color());
         list->prepend(CSSShadowValue::create(WTFMove(x), WTFMove(y), WTFMove(blur), WTFMove(spread), WTFMove(style), WTFMove(color)));
     }
     return WTFMove(list);
@@ -2567,7 +2567,7 @@ RefPtr<CSSValue> ComputedStyleExtractor::propertyValue(CSSPropertyID propertyID,
             break;
 
         case CSSPropertyBackgroundColor:
-            return cssValuePool.createColorValue(m_allowVisitedStyle? style->visitedDependentColor(CSSPropertyBackgroundColor).rgb() : style->backgroundColor().rgb());
+            return cssValuePool.createColorValue(m_allowVisitedStyle? style->visitedDependentColor(CSSPropertyBackgroundColor) : style->backgroundColor());
         case CSSPropertyBackgroundImage:
         case CSSPropertyWebkitMaskImage: {
             const FillLayer* layers = propertyID == CSSPropertyWebkitMaskImage ? style->maskLayers() : style->backgroundLayers();
@@ -2728,13 +2728,13 @@ RefPtr<CSSValue> ComputedStyleExtractor::propertyValue(CSSPropertyID propertyID,
                 return style->borderImageSource()->cssValue();
             return cssValuePool.createIdentifierValue(CSSValueNone);
         case CSSPropertyBorderTopColor:
-            return m_allowVisitedStyle ? cssValuePool.createColorValue(style->visitedDependentColor(CSSPropertyBorderTopColor).rgb()) : currentColorOrValidColor(style, style->borderTopColor());
+            return m_allowVisitedStyle ? cssValuePool.createColorValue(style->visitedDependentColor(CSSPropertyBorderTopColor)) : currentColorOrValidColor(style, style->borderTopColor());
         case CSSPropertyBorderRightColor:
-            return m_allowVisitedStyle ? cssValuePool.createColorValue(style->visitedDependentColor(CSSPropertyBorderRightColor).rgb()) : currentColorOrValidColor(style, style->borderRightColor());
+            return m_allowVisitedStyle ? cssValuePool.createColorValue(style->visitedDependentColor(CSSPropertyBorderRightColor)) : currentColorOrValidColor(style, style->borderRightColor());
         case CSSPropertyBorderBottomColor:
-            return m_allowVisitedStyle ? cssValuePool.createColorValue(style->visitedDependentColor(CSSPropertyBorderBottomColor).rgb()) : currentColorOrValidColor(style, style->borderBottomColor());
+            return m_allowVisitedStyle ? cssValuePool.createColorValue(style->visitedDependentColor(CSSPropertyBorderBottomColor)) : currentColorOrValidColor(style, style->borderBottomColor());
         case CSSPropertyBorderLeftColor:
-            return m_allowVisitedStyle ? cssValuePool.createColorValue(style->visitedDependentColor(CSSPropertyBorderLeftColor).rgb()) : currentColorOrValidColor(style, style->borderLeftColor());
+            return m_allowVisitedStyle ? cssValuePool.createColorValue(style->visitedDependentColor(CSSPropertyBorderLeftColor)) : currentColorOrValidColor(style, style->borderLeftColor());
         case CSSPropertyBorderTopStyle:
             return cssValuePool.createValue(style->borderTopStyle());
         case CSSPropertyBorderRightStyle:
@@ -2785,7 +2785,7 @@ RefPtr<CSSValue> ComputedStyleExtractor::propertyValue(CSSPropertyID propertyID,
         case CSSPropertyClear:
             return cssValuePool.createValue(style->clear());
         case CSSPropertyColor:
-            return cssValuePool.createColorValue(m_allowVisitedStyle ? style->visitedDependentColor(CSSPropertyColor).rgb() : style->color().rgb());
+            return cssValuePool.createColorValue(m_allowVisitedStyle ? style->visitedDependentColor(CSSPropertyColor) : style->color());
         case CSSPropertyWebkitPrintColorAdjust:
             return cssValuePool.createValue(style->printColorAdjust());
         case CSSPropertyWebkitColumnAxis:
@@ -2803,7 +2803,7 @@ RefPtr<CSSValue> ComputedStyleExtractor::propertyValue(CSSPropertyID propertyID,
         case CSSPropertyWebkitColumnProgression:
             return cssValuePool.createValue(style->columnProgression());
         case CSSPropertyColumnRuleColor:
-            return m_allowVisitedStyle ? cssValuePool.createColorValue(style->visitedDependentColor(CSSPropertyOutlineColor).rgb()) : currentColorOrValidColor(style, style->columnRuleColor());
+            return m_allowVisitedStyle ? cssValuePool.createColorValue(style->visitedDependentColor(CSSPropertyOutlineColor)) : currentColorOrValidColor(style, style->columnRuleColor());
         case CSSPropertyColumnRuleStyle:
             return cssValuePool.createValue(style->columnRuleStyle());
         case CSSPropertyColumnRuleWidth:
@@ -3145,7 +3145,7 @@ RefPtr<CSSValue> ComputedStyleExtractor::propertyValue(CSSPropertyID propertyID,
                 return cssValuePool.createIdentifierValue(CSSValueAuto);
             return cssValuePool.createValue(style->orphans(), CSSPrimitiveValue::CSS_NUMBER);
         case CSSPropertyOutlineColor:
-            return m_allowVisitedStyle ? cssValuePool.createColorValue(style->visitedDependentColor(CSSPropertyOutlineColor).rgb()) : currentColorOrValidColor(style, style->outlineColor());
+            return m_allowVisitedStyle ? cssValuePool.createColorValue(style->visitedDependentColor(CSSPropertyOutlineColor)) : currentColorOrValidColor(style, style->outlineColor());
         case CSSPropertyOutlineOffset:
             return zoomAdjustedPixelValue(style->outlineOffset(), *style);
         case CSSPropertyOutlineStyle:
index 0591ab3..396cb3a 100644 (file)
@@ -1204,6 +1204,7 @@ rgb
 rgba
 hsl
 hsla
+//color
 
 // transform
 matrix
@@ -1310,6 +1311,10 @@ last
 p3
 rec2020
 
+// color() function
+sRGB
+display-p3
+
 // prefers-reduced-motion
 reduce
 
index 564ae6d..e1b03d0 100644 (file)
@@ -176,7 +176,7 @@ new
 // CSS_PROP_STOP_OPACITY
 // CSS_PROP_COLOR_INTERPOLATION
 //auto
-sRGB
+//sRGB
 linearRGB
 
 // CSS_PROP_COLOR_INTERPOLATION_FILTERS
index a953224..6e80c35 100644 (file)
@@ -7608,21 +7608,22 @@ bool CSSParser::isCalculation(CSSParserValue& value)
             || equalLettersIgnoringASCIICase(value.function->name, "-webkit-calc("));
 }
 
-inline int CSSParser::colorIntFromValue(ValueWithCalculation& valueWithCalculation)
+static bool isPercent(const CSSParser::ValueWithCalculation& valueWithCalculation)
 {
-    bool isPercent;
-    
     if (valueWithCalculation.calculation())
-        isPercent = valueWithCalculation.calculation()->category() == CalcPercent;
-    else
-        isPercent = valueWithCalculation.value().unit == CSSPrimitiveValue::CSS_PERCENTAGE;
+        return valueWithCalculation.calculation()->category() == CalcPercent;
 
-    const double doubleValue = parsedDouble(valueWithCalculation);
+    return valueWithCalculation.value().unit == CSSPrimitiveValue::CSS_PERCENTAGE;
+}
+
+inline int CSSParser::parseColorInt(ValueWithCalculation& valueWithCalculation)
+{
+    double doubleValue = parsedDouble(valueWithCalculation);
     
     if (doubleValue <= 0.0)
         return 0;
 
-    if (isPercent) {
+    if (isPercent(valueWithCalculation)) {
         if (doubleValue >= 100.0)
             return 255;
         return static_cast<int>(doubleValue * 256.0 / 100.0);
@@ -7634,6 +7635,16 @@ inline int CSSParser::colorIntFromValue(ValueWithCalculation& valueWithCalculati
     return static_cast<int>(doubleValue);
 }
 
+inline double CSSParser::parseColorDouble(ValueWithCalculation& valueWithCalculation)
+{
+    double doubleValue = parsedDouble(valueWithCalculation);
+
+    if (isPercent(valueWithCalculation))
+        return doubleValue / 100.0;
+
+    return doubleValue;
+}
+
 bool CSSParser::parseRGBParameters(CSSParserValue& value, int* colorArray, bool parseAlpha)
 {
     CSSParserValueList* args = value.function->args.get();
@@ -7647,7 +7658,7 @@ bool CSSParser::parseRGBParameters(CSSParserValue& value, int* colorArray, bool
     else
         return false;
     
-    colorArray[0] = colorIntFromValue(firstArgumentWithCalculation);
+    colorArray[0] = parseColorInt(firstArgumentWithCalculation);
     for (int i = 1; i < 3; i++) {
         CSSParserValue& operatorArgument = *args->next();
         if (operatorArgument.unit != CSSParserValue::Operator && operatorArgument.iValue != ',')
@@ -7655,7 +7666,7 @@ bool CSSParser::parseRGBParameters(CSSParserValue& value, int* colorArray, bool
         ValueWithCalculation argumentWithCalculation(*args->next());
         if (!validateUnit(argumentWithCalculation, unitType, HTMLStandardMode))
             return false;
-        colorArray[i] = colorIntFromValue(argumentWithCalculation);
+        colorArray[i] = parseColorInt(argumentWithCalculation);
     }
     if (parseAlpha) {
         CSSParserValue& operatorArgument = *args->next();
@@ -7667,11 +7678,65 @@ bool CSSParser::parseRGBParameters(CSSParserValue& value, int* colorArray, bool
         double doubleValue = parsedDouble(argumentWithCalculation);
         // Convert the floating pointer number of alpha to an integer in the range [0, 256),
         // with an equal distribution across all 256 values.
-        colorArray[3] = static_cast<int>(std::max<double>(0, std::min<double>(1, doubleValue)) * nextafter(256.0, 0.0));
+        colorArray[3] = static_cast<int>(std::max(0.0, std::min(1.0, doubleValue)) * nextafter(256.0, 0.0));
     }
     return true;
 }
 
+Optional<std::pair<std::array<double, 4>, ColorSpace>> CSSParser::parseColorFunctionParameters(CSSParserValue& value)
+{
+    CSSParserValueList* args = value.function->args.get();
+    if (!args->size())
+        return Nullopt;
+
+    ColorSpace colorSpace;
+    switch (args->current()->id) {
+    case CSSValueSrgb:
+        colorSpace = ColorSpaceSRGB;
+        break;
+    case CSSValueDisplayP3:
+        colorSpace = ColorSpaceDisplayP3;
+        break;
+    default:
+        return Nullopt;
+    }
+
+    std::array<double, 4> colorValues = { { 0, 0, 0, 1 } };
+
+    for (int i = 0; i < 3; ++i) {
+        auto valueOrNull = args->next();
+        if (valueOrNull) {
+            ValueWithCalculation argumentWithCalculation(*valueOrNull);
+            if (!validateUnit(argumentWithCalculation, FNumber))
+                return Nullopt;
+            colorValues[i] = std::max(0.0, std::min(1.0, parsedDouble(argumentWithCalculation)));
+        }
+    }
+
+    auto slashOrNull = args->next();
+    if (!slashOrNull)
+        return { { colorValues, colorSpace } };
+
+    if (!isForwardSlashOperator(*slashOrNull))
+        return Nullopt;
+
+    // Handle alpha.
+
+    ValueWithCalculation argumentWithCalculation(*args->next());
+    if (!validateUnit(argumentWithCalculation, FNumber | FPercent))
+        return Nullopt;
+    colorValues[3] = std::max(0.0, std::min(1.0, parseColorDouble(argumentWithCalculation)));
+
+    // FIXME: Support the comma-separated list of fallback color values.
+    // If there is another argument, it should be a comma.
+
+    auto commaOrNull = args->next();
+    if (commaOrNull && !isComma(commaOrNull))
+        return Nullopt;
+
+    return { { colorValues, colorSpace } };
+}
+
 // The CSS3 specification defines the format of a HSL color as
 // hsl(<number>, <percent>, <percent>)
 // and with alpha, the format is
@@ -7693,7 +7758,7 @@ bool CSSParser::parseHSLParameters(CSSParserValue& value, double* colorArray, bo
         ValueWithCalculation argumentWithCalculation(*args->next());
         if (!validateUnit(argumentWithCalculation, FPercent, HTMLStandardMode))
             return false;
-        colorArray[i] = std::max<double>(0, std::min<double>(100, parsedDouble(argumentWithCalculation))) / 100.0; // needs to be value between 0 and 1.0
+        colorArray[i] = std::max(0.0, std::min(100.0, parsedDouble(argumentWithCalculation))) / 100.0; // needs to be value between 0 and 1.0
     }
     if (parseAlpha) {
         CSSParserValue& operatorArgument = *args->next();
@@ -7702,7 +7767,7 @@ bool CSSParser::parseHSLParameters(CSSParserValue& value, double* colorArray, bo
         ValueWithCalculation argumentWithCalculation(*args->next());
         if (!validateUnit(argumentWithCalculation, FNumber, HTMLStandardMode))
             return false;
-        colorArray[3] = std::max<double>(0, std::min<double>(1, parsedDouble(argumentWithCalculation)));
+        colorArray[3] = std::max(0.0, std::min(1.0, parsedDouble(argumentWithCalculation)));
     }
     return true;
 }
@@ -7758,6 +7823,13 @@ Color CSSParser::parseColorFromValue(CSSParserValue& value)
         if (!parseHSLParameters(value, colorValues, true))
             return Color();
         return Color(makeRGBAFromHSLA(colorValues[0], colorValues[1], colorValues[2], colorValues[3]));
+    } else if (value.unit == CSSParserValue::Function
+        && value.function->args
+        && equalLettersIgnoringASCIICase(value.function->name, "color(")) {
+        Optional<std::pair<std::array<double, 4>, ColorSpace>> colorData = parseColorFunctionParameters(value);
+        if (!colorData)
+            return Color();
+        return Color(colorData.value().first[0], colorData.value().first[1], colorData.value().first[2], colorData.value().first[3], colorData.value().second);
     }
 
     return Color();
index ee66213..9133fbc 100644 (file)
@@ -31,6 +31,7 @@
 #include "CSSPropertySourceData.h"
 #include "CSSValueKeywords.h"
 #include "Color.h"
+#include "ColorSpace.h"
 #include "MediaQuery.h"
 #include "StyleRuleImport.h"
 #include "WebKitCSSFilterValue.h"
@@ -275,6 +276,7 @@ public:
 
     bool parseRGBParameters(CSSParserValue&, int* colorValues, bool parseAlpha);
     bool parseHSLParameters(CSSParserValue&, double* colorValues, bool parseAlpha);
+    Optional<std::pair<std::array<double, 4>, ColorSpace>> parseColorFunctionParameters(CSSParserValue&);
     RefPtr<CSSPrimitiveValue> parseColor(CSSParserValue* = nullptr);
     Color parseColorFromValue(CSSParserValue&);
     void parseSelector(const String&, CSSSelectorList&);
@@ -708,7 +710,8 @@ private:
     bool validateUnit(ValueWithCalculation&, Units, CSSParserMode);
 
     bool parseBorderImageQuad(Units, RefPtr<CSSPrimitiveValue>&);
-    int colorIntFromValue(ValueWithCalculation&);
+    int parseColorInt(ValueWithCalculation&);
+    double parseColorDouble(ValueWithCalculation&);
     double parsedDouble(ValueWithCalculation&);
     
     friend class TransformOperationInfo;
index a59f0b1..66c0440 100644 (file)
@@ -464,6 +464,51 @@ static Color parseHSLParameters(CSSParserTokenRange& range, bool parseAlpha)
     return Color(makeRGBAFromHSLA(colorArray[0], colorArray[1], colorArray[2], alpha));
 }
 
+static Color parseColorFunctionParameters(CSSParserTokenRange& range)
+{
+    ASSERT(range.peek().functionId() == CSSValueColor);
+    CSSParserTokenRange args = consumeFunction(range);
+
+    ColorSpace colorSpace;
+    switch (args.peek().id()) {
+    case CSSValueSrgb:
+        colorSpace = ColorSpaceSRGB;
+        break;
+    case CSSValueDisplayP3:
+        colorSpace = ColorSpaceDisplayP3;
+        break;
+    default:
+        return Color();
+    }
+    consumeIdent(args);
+
+    double colorChannels[4] = { 0, 0, 0, 1 };
+    for (int i = 0; i < 3; ++i) {
+        double value;
+        if (consumeNumberRaw(args, value))
+            colorChannels[i] = std::max(0.0, std::min(1.0, value));
+        else
+            break;
+    }
+
+    if (consumeSlashIncludingWhitespace(args)) {
+        auto alphaParameter = consumePercent(args, ValueRangeAll);
+        if (!alphaParameter)
+            alphaParameter = consumeNumber(args, ValueRangeAll);
+        if (!alphaParameter)
+            return Color();
+
+        colorChannels[3] = std::max(0.0, std::min(1.0, alphaParameter->isPercentage() ? (alphaParameter->doubleValue() / 100) : alphaParameter->doubleValue()));
+    }
+
+    // FIXME: Support the comma-separated list of fallback color values.
+
+    if (!args.atEnd())
+        return Color();
+    
+    return Color(colorChannels[0], colorChannels[1], colorChannels[2], colorChannels[3], colorSpace);
+}
+
 static Color parseHexColor(CSSParserTokenRange& range, bool acceptQuirkyColors)
 {
     RGBA32 result;
@@ -513,8 +558,8 @@ static Color parseColorFunction(CSSParserTokenRange& range)
         color = parseHSLParameters(colorRange, functionId == CSSValueHsla);
         break;
     case CSSValueColor:
-        // FIXME-NEWPARSER: Add support for color().
-        return Color();
+        color = parseColorFunctionParameters(colorRange);
+        break;
     default:
         return Color();
     }
index 793c8d5..637395c 100644 (file)
@@ -196,7 +196,7 @@ public:
     // The latter format is not a valid CSS color, and should only be seen in DRT dumps.
     String nameForRenderTreeAsText() const;
 
-    bool isValid() const { return m_colorData.rgbaAndFlags & validRGBAColorBit; }
+    bool isValid() const { return isExtended() || (m_colorData.rgbaAndFlags & validRGBAColorBit); }
 
     bool hasAlpha() const { return alpha() < 255; }
 
@@ -205,7 +205,7 @@ public:
     int blue() const { return blueChannel(rgb()); }
     int alpha() const { return alphaChannel(rgb()); }
     
-    RGBA32 rgb() const { ASSERT(!isExtended()); return static_cast<RGBA32>(m_colorData.rgbaAndFlags >> 32); }
+    RGBA32 rgb() const;
 
     // FIXME: Like operator==, this will give different values for ExtendedColors that
     // should be identical, since the respective pointer will be different.
@@ -397,6 +397,14 @@ inline RGBA32 colorWithOverrideAlpha(RGBA32 color, Optional<float> overrideAlpha
     return overrideAlpha ? colorWithOverrideAlpha(color, overrideAlpha.value()) : color;
 }
 
+inline RGBA32 Color::rgb() const
+{
+    // FIXME: We should ASSERT(!isExtended()) here, or produce
+    // an RGBA32 equivalent for an ExtendedColor. Ideally the former,
+    // so we can audit all the rgb() call sites to handle extended.
+    return static_cast<RGBA32>(m_colorData.rgbaAndFlags >> 32);
+}
+
 inline void Color::setRGB(RGBA32 rgb)
 {
     m_colorData.rgbaAndFlags = static_cast<uint64_t>(rgb) << 32;
index bf13a23..50e3c5c 100644 (file)
@@ -66,9 +66,10 @@ String ExtendedColor::cssText() const
     builder.append(' ');
 
     builder.append(numberToFixedPrecisionString(blue(), 6, buffer, shouldTruncateTrailingZeros));
-    builder.appendLiteral(" / ");
-
-    builder.append(numberToFixedPrecisionString(alpha(), 6, buffer, shouldTruncateTrailingZeros));
+    if (!WTF::areEssentiallyEqual(alpha(), 1.0f)) {
+        builder.appendLiteral(" / ");
+        builder.append(numberToFixedPrecisionString(alpha(), 6, buffer, shouldTruncateTrailingZeros));
+    }
     builder.append(')');
 
     return builder.toString();
index 8b2cca2..1dcfecd 100644 (file)
@@ -116,28 +116,48 @@ Color::Color(CGColorRef color)
 static CGColorRef leakCGColor(const Color& color)
 {
     CGFloat components[4];
+    if (color.isExtended()) {
+        ExtendedColor& extendedColor = color.asExtended();
+        components[0] = extendedColor.red();
+        components[1] = extendedColor.green();
+        components[2] = extendedColor.blue();
+        components[3] = extendedColor.alpha();
+        switch (extendedColor.colorSpace()) {
+        case ColorSpaceSRGB:
+            return CGColorCreate(sRGBColorSpaceRef(), components);
+        case ColorSpaceDisplayP3:
+            return CGColorCreate(displayP3ColorSpaceRef(), components);
+        case ColorSpaceLinearRGB:
+        case ColorSpaceDeviceRGB:
+            // FIXME: Do we ever create CGColorRefs in these spaces? It may only be ImageBuffers.
+            return CGColorCreate(sRGBColorSpaceRef(), components);
+        }
+    }
+
     color.getRGBA(components[0], components[1], components[2], components[3]);
     return CGColorCreate(sRGBColorSpaceRef(), components);
 }
 
 CGColorRef cachedCGColor(const Color& color)
 {
-    switch (color.rgb()) {
-    case Color::transparent: {
-        static CGColorRef transparentCGColor = leakCGColor(color);
-        return transparentCGColor;
-    }
-    case Color::black: {
-        static CGColorRef blackCGColor = leakCGColor(color);
-        return blackCGColor;
-    }
-    case Color::white: {
-        static CGColorRef whiteCGColor = leakCGColor(color);
-        return whiteCGColor;
-    }
+    if (!color.isExtended()) {
+        switch (color.rgb()) {
+        case Color::transparent: {
+            static CGColorRef transparentCGColor = leakCGColor(color);
+            return transparentCGColor;
+        }
+        case Color::black: {
+            static CGColorRef blackCGColor = leakCGColor(color);
+            return blackCGColor;
+        }
+        case Color::white: {
+            static CGColorRef whiteCGColor = leakCGColor(color);
+            return whiteCGColor;
+        }
+        }
     }
 
-    ASSERT(color.rgb());
+    ASSERT(color.isExtended() || color.rgb());
 
     static NeverDestroyed<TinyLRUCache<Color, RetainPtr<CGColorRef>, 32>> cache;
     return cache.get().get(color).get();