Skip to content

Commit 71d0f15

Browse files
author
Sam Weinig
committed
Update color-contrast() to support a target contrast ratio
https://bugs.webkit.org/show_bug.cgi?id=224411 Reviewed by Antti Koivisto. Source/WebCore: Updated fast/css/parsing-color-contrast.html with new test cases. * css/CSSValueKeywords.in: Add new keywords, AA and AA-large which are constants for contrast ratios of 4.5 and 3.0 respectively. * css/parser/CSSPropertyParserHelpers.cpp: (WebCore::CSSPropertyParserHelpers::parseColorFunctionParameters): Remove now incorrect comment about adding support for fallback as fallback has been removed from the spec. (WebCore::CSSPropertyParserHelpers::selectFirstColorThatMeetsOrExceedsTargetContrast): (WebCore::CSSPropertyParserHelpers::selectFirstColorWithHighestContrast): (WebCore::CSSPropertyParserHelpers::parseColorContrastFunctionParameters): Add support for the new optional target contrast ratio in color-contrast() that was recently added to CSS Color 5. LayoutTests: Add tests and update results for target contrast ratios in color-contrast(). * fast/css/parsing-color-contrast-expected.txt: * fast/css/parsing-color-contrast.html: Canonical link: https://commits.webkit.org/236396@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@275826 268f45cc-cd09-0410-ab3c-d52691b4dbfc
1 parent f5f1475 commit 71d0f15

6 files changed

Lines changed: 128 additions & 14 deletions

File tree

LayoutTests/ChangeLog

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,15 @@
1+
2021-04-12 Sam Weinig <weinig@apple.com>
2+
3+
Update color-contrast() to support a target contrast ratio
4+
https://bugs.webkit.org/show_bug.cgi?id=224411
5+
6+
Reviewed by Antti Koivisto.
7+
8+
Add tests and update results for target contrast ratios in color-contrast().
9+
10+
* fast/css/parsing-color-contrast-expected.txt:
11+
* fast/css/parsing-color-contrast.html:
12+
113
2021-04-12 Antti Koivisto <antti@apple.com>
214

315
Descendant style relations are sometimes reset when they shouldn't

LayoutTests/fast/css/parsing-color-contrast-expected.txt

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ Test the parsing of CSS Color 5 color-contrast().
33
On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
44

55

6-
color-contrast(... vs ..., ...)
6+
color-contrast(... vs ..., ... [to ...])
7+
8+
Test with no specified target contrast
79
PASS computedStyle("background-color", "color-contrast(white vs red, blue)") is "rgb(0, 0, 255)"
810
PASS computedStyle("background-color", "color-contrast(white vs blue, red)") is "rgb(0, 0, 255)"
911
PASS computedStyle("background-color", "color-contrast(white vs red, blue, green)") is "rgb(0, 0, 255)"
@@ -15,6 +17,15 @@ PASS computedStyle("background-color", "color-contrast(black vs blue, red)") is
1517
PASS computedStyle("background-color", "color-contrast(black vs white, white)") is "rgb(255, 255, 255)"
1618
PASS computedStyle("background-color", "color-contrast(red vs blue, rgb(255, 255, 255, .5))") is "rgba(255, 255, 255, 0.5)"
1719

20+
Test with specified target contrast
21+
PASS computedStyle("background-color", "color-contrast(wheat vs bisque, darkgoldenrod, olive, sienna, darkgreen, maroon to AA)") is "rgb(0, 100, 0)"
22+
PASS computedStyle("background-color", "color-contrast(wheat vs bisque, darkgoldenrod, olive, sienna, darkgreen, maroon to AA-large)") is "rgb(128, 128, 0)"
23+
PASS computedStyle("background-color", "color-contrast(wheat vs bisque, darkgoldenrod, olive, sienna, darkgreen, maroon to 5.8)") is "rgb(128, 0, 0)"
24+
25+
Test with specified target contrast that none meet
26+
PASS computedStyle("background-color", "color-contrast(wheat vs bisque, darkgoldenrod, olive to 100)") is "rgb(0, 0, 0)"
27+
PASS computedStyle("background-color", "color-contrast(green vs bisque, darkgoldenrod, olive to 100)") is "rgb(255, 255, 255)"
28+
1829
Test non-sRGB colors
1930
PASS computedStyle("background-color", "color-contrast(green vs color(display-p3 0 1 0), color(display-p3 0 0 1))") is "color(display-p3 0 1 0)"
2031
PASS computedStyle("background-color", "color-contrast(color(display-p3 1 1 0) vs color(display-p3 0 1 0), color(display-p3 0 0 1))") is "color(display-p3 0 0 1)"
@@ -27,6 +38,9 @@ PASS computedStyle("background-color", "color-contrast(white vs red,)") is "rgba
2738
PASS computedStyle("background-color", "color-contrast(white vs )") is "rgba(0, 0, 0, 0)"
2839
PASS computedStyle("background-color", "color-contrast(white)") is "rgba(0, 0, 0, 0)"
2940
PASS computedStyle("background-color", "color-contrast(white vs red green)") is "rgba(0, 0, 0, 0)"
41+
PASS computedStyle("background-color", "color-contrast(white vs red, green to)") is "rgba(0, 0, 0, 0)"
42+
PASS computedStyle("background-color", "color-contrast(white vs red, green to invalid)") is "rgba(0, 0, 0, 0)"
43+
PASS computedStyle("background-color", "color-contrast(white vs red to AA)") is "rgba(0, 0, 0, 0)"
3044
PASS successfullyParsed is true
3145

3246
TEST COMPLETE

LayoutTests/fast/css/parsing-color-contrast.html

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,10 @@
2626
testComputedProperty("background-color", value, expected);
2727
}
2828

29-
debug('color-contrast(... vs ..., ...)');
29+
debug('color-contrast(... vs ..., ... [to ...])');
30+
31+
debug('');
32+
debug('Test with no specified target contrast')
3033

3134
testComputed(`color-contrast(white vs red, blue)`, `rgb(0, 0, 255)`);
3235
testComputed(`color-contrast(white vs blue, red)`, `rgb(0, 0, 255)`);
@@ -39,6 +42,19 @@
3942
testComputed(`color-contrast(black vs white, white)`, `rgb(255, 255, 255)`);
4043
testComputed(`color-contrast(red vs blue, rgb(255, 255, 255, .5))`, `rgba(255, 255, 255, 0.5)`);
4144

45+
debug('');
46+
debug('Test with specified target contrast')
47+
48+
testComputed(`color-contrast(wheat vs bisque, darkgoldenrod, olive, sienna, darkgreen, maroon to AA)`, `rgb(0, 100, 0)`); // darkgreen
49+
testComputed(`color-contrast(wheat vs bisque, darkgoldenrod, olive, sienna, darkgreen, maroon to AA-large)`, `rgb(128, 128, 0)`); // olive
50+
testComputed(`color-contrast(wheat vs bisque, darkgoldenrod, olive, sienna, darkgreen, maroon to 5.8)`, `rgb(128, 0, 0)`); // olive maroon
51+
52+
debug('');
53+
debug('Test with specified target contrast that none meet')
54+
testComputed(`color-contrast(wheat vs bisque, darkgoldenrod, olive to 100)`, `rgb(0, 0, 0)`); // black
55+
testComputed(`color-contrast(green vs bisque, darkgoldenrod, olive to 100)`, `rgb(255, 255, 255)`); // white
56+
57+
4258
debug('');
4359
debug('Test non-sRGB colors');
4460

@@ -55,6 +71,9 @@
5571
testComputed(`color-contrast(white vs )`, `rgba(0, 0, 0, 0)`);
5672
testComputed(`color-contrast(white)`, `rgba(0, 0, 0, 0)`);
5773
testComputed(`color-contrast(white vs red green)`, `rgba(0, 0, 0, 0)`);
74+
testComputed(`color-contrast(white vs red, green to)`, `rgba(0, 0, 0, 0)`);
75+
testComputed(`color-contrast(white vs red, green to invalid)`, `rgba(0, 0, 0, 0)`);
76+
testComputed(`color-contrast(white vs red to AA)`, `rgba(0, 0, 0, 0)`);
5877

5978
</script>
6079

Source/WebCore/ChangeLog

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,27 @@
1+
2021-04-12 Sam Weinig <weinig@apple.com>
2+
3+
Update color-contrast() to support a target contrast ratio
4+
https://bugs.webkit.org/show_bug.cgi?id=224411
5+
6+
Reviewed by Antti Koivisto.
7+
8+
Updated fast/css/parsing-color-contrast.html with new test cases.
9+
10+
* css/CSSValueKeywords.in:
11+
Add new keywords, AA and AA-large which are constants for contrast ratios of 4.5
12+
and 3.0 respectively.
13+
14+
* css/parser/CSSPropertyParserHelpers.cpp:
15+
(WebCore::CSSPropertyParserHelpers::parseColorFunctionParameters):
16+
Remove now incorrect comment about adding support for fallback as fallback
17+
has been removed from the spec.
18+
19+
(WebCore::CSSPropertyParserHelpers::selectFirstColorThatMeetsOrExceedsTargetContrast):
20+
(WebCore::CSSPropertyParserHelpers::selectFirstColorWithHighestContrast):
21+
(WebCore::CSSPropertyParserHelpers::parseColorContrastFunctionParameters):
22+
Add support for the new optional target contrast ratio in color-contrast() that was
23+
recently added to CSS Color 5.
24+
125
2021-04-12 Antti Koivisto <antti@apple.com>
226

327
Descendant style relations are sometimes reset when they shouldn't

Source/WebCore/css/CSSValueKeywords.in

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1438,6 +1438,8 @@ xyz
14381438
// color-contast()
14391439
color-contrast
14401440
vs
1441+
AA
1442+
AA-large
14411443

14421444
// color-mix()
14431445
color-mix

Source/WebCore/css/parser/CSSPropertyParserHelpers.cpp

Lines changed: 55 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1502,15 +1502,47 @@ static Color parseColorFunctionParameters(CSSParserTokenRange& range, const CSSP
15021502
if (!color.isValid())
15031503
return { };
15041504

1505-
// FIXME: Support the comma-separated list of fallback color values.
1506-
15071505
if (!args.atEnd())
15081506
return { };
15091507

15101508
ASSERT(color.usesColorFunctionSerialization());
15111509
return color;
15121510
}
15131511

1512+
static Color selectFirstColorThatMeetsOrExceedsTargetContrast(const Color& originBackgroundColor, const Vector<Color>& colorsToCompareAgainst, double targetContrast)
1513+
{
1514+
auto originBackgroundColorLuminance = originBackgroundColor.luminance();
1515+
1516+
for (auto& color : colorsToCompareAgainst) {
1517+
auto contrastRatio = WebCore::contrastRatio(originBackgroundColorLuminance, color.luminance());
1518+
if (contrastRatio >= targetContrast)
1519+
return color;
1520+
}
1521+
1522+
// If there is a target contrast, and the end of the list is reached without meeting that target,
1523+
// either white or black is returned, whichever has the higher contrast.
1524+
auto contrastRatioWithWhite = WebCore::contrastRatio(originBackgroundColorLuminance, 1.0f);
1525+
auto contrastRatioWithBlack = WebCore::contrastRatio(originBackgroundColorLuminance, 0.0f);
1526+
return contrastRatioWithWhite > contrastRatioWithBlack ? Color::white : Color::black;
1527+
}
1528+
1529+
static Color selectFirstColorWithHighestContrast(const Color& originBackgroundColor, const Vector<Color>& colorsToCompareAgainst)
1530+
{
1531+
auto originBackgroundColorLuminance = originBackgroundColor.luminance();
1532+
1533+
size_t indexOfColorWithHigestContrastRatio = 0;
1534+
float highestContrastRatioSoFar = 0;
1535+
for (size_t i = 0; i < colorsToCompareAgainst.size(); ++i) {
1536+
auto contrastRatio = WebCore::contrastRatio(originBackgroundColorLuminance, colorsToCompareAgainst[i].luminance());
1537+
if (contrastRatio > highestContrastRatioSoFar) {
1538+
highestContrastRatioSoFar = contrastRatio;
1539+
indexOfColorWithHigestContrastRatio = i;
1540+
}
1541+
}
1542+
1543+
return colorsToCompareAgainst[indexOfColorWithHigestContrastRatio];
1544+
}
1545+
15141546
static Color parseColorContrastFunctionParameters(CSSParserTokenRange& range, const CSSParserContext& context)
15151547
{
15161548
ASSERT(range.peek().functionId() == CSSValueColorContrast);
@@ -1528,30 +1560,41 @@ static Color parseColorContrastFunctionParameters(CSSParserTokenRange& range, co
15281560
return { };
15291561

15301562
Vector<Color> colorsToCompareAgainst;
1563+
bool consumedTo = false;
15311564
do {
15321565
auto colorToCompareAgainst = consumeOriginColor(args, context);
15331566
if (!colorToCompareAgainst.isValid())
15341567
return { };
15351568

15361569
colorsToCompareAgainst.append(WTFMove(colorToCompareAgainst));
1570+
1571+
if (consumeIdentRaw<CSSValueTo>(args)) {
1572+
consumedTo = true;
1573+
break;
1574+
}
15371575
} while (consumeCommaIncludingWhitespace(args));
15381576

15391577
if (colorsToCompareAgainst.size() == 1)
15401578
return { };
15411579

1542-
auto originBackgroundColorLuminance = originBackgroundColor.luminance();
1580+
if (consumedTo) {
1581+
auto targetContrast = [&] () -> Optional<double> {
1582+
if (consumeIdentRaw<CSSValueAA>(args))
1583+
return 4.5;
1584+
if (consumeIdentRaw<CSSValueAALarge>(args))
1585+
return 3.0;
1586+
return consumeNumberRaw(args);
1587+
}();
15431588

1544-
size_t indexOfColorWithHigestContrastRatio = 0;
1545-
float highestContrastRatioSoFar = 0;
1546-
for (size_t i = 0; i < colorsToCompareAgainst.size(); ++i) {
1547-
auto contrastRatio = WebCore::contrastRatio(originBackgroundColorLuminance, colorsToCompareAgainst[i].luminance());
1548-
if (contrastRatio > highestContrastRatioSoFar) {
1549-
highestContrastRatioSoFar = contrastRatio;
1550-
indexOfColorWithHigestContrastRatio = i;
1551-
}
1589+
if (!targetContrast)
1590+
return { };
1591+
1592+
// When a target constast is specified, we select "the first color color to meet or exceed the target contrast."
1593+
return selectFirstColorThatMeetsOrExceedsTargetContrast(originBackgroundColor, colorsToCompareAgainst, *targetContrast);
15521594
}
15531595

1554-
return colorsToCompareAgainst[indexOfColorWithHigestContrastRatio];
1596+
// When a target constast is NOT specified, we select "the first color with the highest contrast to the single color."
1597+
return selectFirstColorWithHighestContrast(originBackgroundColor, colorsToCompareAgainst);
15551598
}
15561599

15571600
enum class ColorMixColorSpace {

0 commit comments

Comments
 (0)