Commit f8e89bb9 authored by Chris Nardi's avatar Chris Nardi Committed by Commit Bot

Parse decimals in RGB/RGBA and update rounding

CSS Color 4 states that all numbers should be parsed inside of the RGB
and RGBA functions, instead of only integers as previously defined.
This change updates our behavior to match this, with non-integers
rounded to the nearest value. This additionally changes the behavior
of percentage values, multiplying by 255 and rounding to match the spec
instead of multiplying by 256 and flooring. The new spec is located at
https://drafts.csswg.org/css-color-4/#rgb-functions.

Bug: 703898
Change-Id: I826e92e7f81576240ad9b06f866df3300140b12c
Reviewed-on: https://chromium-review.googlesource.com/885004Reviewed-by: default avatarEric Willigers <ericwilligers@chromium.org>
Reviewed-by: default avatarDmitry Gozman <dgozman@chromium.org>
Commit-Queue: Chris Nardi <cnardi@chromium.org>
Cr-Commit-Position: refs/heads/master@{#532032}
parent 484efec4
......@@ -1809,16 +1809,6 @@ external/wpt/css/css-color/lch-005.html [ WontFix ]
external/wpt/css/css-color/lch-006.html [ WontFix ]
external/wpt/css/css-color/lch-007.html [ WontFix ]
# New rgb() and rgba() syntax not supported. https://crbug.com/788707
external/wpt/css/css-color/rgb-002.html [ WontFix ]
external/wpt/css/css-color/rgb-004.html [ WontFix ]
external/wpt/css/css-color/rgb-006.html [ WontFix ]
external/wpt/css/css-color/rgb-008.html [ WontFix ]
external/wpt/css/css-color/rgba-002.html [ WontFix ]
external/wpt/css/css-color/rgba-004.html [ WontFix ]
external/wpt/css/css-color/rgba-006.html [ WontFix ]
external/wpt/css/css-color/rgba-008.html [ WontFix ]
# Failures on the initial import of css-color. Test suite had many other bugs,
# so these may also be incorrect tests, but probably some real issues too.
external/wpt/css/css-color/t32-opacity-offscreen-multiple-boxes-1-c.xht [ WontFix ]
......@@ -1827,9 +1817,6 @@ external/wpt/css/css-color/t422-rgba-onscreen-b.xht [ WontFix ]
external/wpt/css/css-color/t422-rgba-onscreen-multiple-boxes-c.xht [ WontFix ]
external/wpt/css/css-color/t425-hsla-onscreen-multiple-boxes-c.xht [ WontFix ]
# Percentages are rounded incorrectly in rgb
external/wpt/css/css-color/color-resolving.html [ WontFix ]
# https://github.com/w3c/web-platform-tests/issues/8547
external/wpt/css/mediaqueries/device-aspect-ratio-001.html [ WontFix ]
external/wpt/css/mediaqueries/device-aspect-ratio-005.html [ WontFix ]
......
......@@ -2476,8 +2476,6 @@ crbug.com/709227 external/wpt/console/console-timeline-timelineEnd-historical.an
crbug.com/709227 external/wpt/html/browsers/history/the-location-interface/per-global.window.html [ Failure ]
crbug.com/709227 external/wpt/html/webappapis/scripting/events/event-handler-processing-algorithm-error/synthetic-errorevent-click.worker.html [ Failure ]
crbug.com/709227 external/wpt/html/webappapis/scripting/events/event-handler-processing-algorithm-error/workerglobalscope-synthetic-errorevent.worker.html [ Failure ]
crbug.com/709227 external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgb-1.worker.html [ Failure ]
crbug.com/709227 external/wpt/offscreen-canvas/fill-and-stroke-styles/2d.fillStyle.parse.css-color-4-rgba-1.worker.html [ Failure ]
crbug.com/709227 external/wpt/offscreen-canvas/path-objects/2d.path.stroke.prune.arc.worker.html [ Failure ]
crbug.com/709227 external/wpt/offscreen-canvas/path-objects/2d.path.stroke.prune.closed.worker.html [ Failure ]
crbug.com/709227 external/wpt/offscreen-canvas/path-objects/2d.path.stroke.prune.curve.worker.html [ Failure ]
......
......@@ -4,10 +4,10 @@ On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE
PASS getComputedStyle(document.getElementById("simple"), null).color is "rgb(10, 180, 30)"
PASS getComputedStyle(document.getElementById("percentsimple"), null).color is "rgb(25, 243, 76)"
PASS getComputedStyle(document.getElementById("percentsimple"), null).color is "rgb(26, 242, 77)"
FAIL getComputedStyle(document.getElementById("percentnumber"), null).color should be rgb(26, 240, 80). Was rgb(0, 0, 0).
PASS getComputedStyle(document.getElementById("alpha"), null).color is "rgba(10, 180, 30, 0.7)"
PASS getComputedStyle(document.getElementById("percentalpha"), null).color is "rgba(25, 243, 76, 0.7)"
PASS getComputedStyle(document.getElementById("percentalpha"), null).color is "rgba(26, 242, 77, 0.7)"
FAIL getComputedStyle(document.getElementById("percentnumberalpha"), null).color should be rgba(26, 240, 80, 0.5). Was rgb(0, 0, 0).
PASS successfullyParsed is true
......
This is a testharness.js-based test.
FAIL Canvas test: 2d.fillStyle.parse.css-color-4-rgb-1 assert_equals: Red channel of the pixel at (50, 25) expected 0 but got 255
Harness: the test ran to completion.
This is a testharness.js-based test.
FAIL Canvas test: 2d.fillStyle.parse.css-color-4-rgba-1 assert_equals: Red channel of the pixel at (50, 25) expected 0 but got 255
Harness: the test ran to completion.
......@@ -6,7 +6,7 @@
<link rel="match" href="greentext-ref.html">
<meta name="assert" content="rgb() with 8-bit numbers and no alpha, also no comma">
<style>
.test {color: rgb(0 80.0 0)}
.test {color: rgb(0 128.0 0)}
</style>
<body>
<p class="test">Test passes if this text is green</p>
......
......@@ -6,7 +6,7 @@
<link rel="match" href="greentext-ref.html">
<meta name="assert" content="rgb() with 8-bit numbers and numeric alpha, also no comma">
<style>
.test {color: rgb(0 80.0 0 / 1)}
.test {color: rgb(0 128.0 0 / 1)}
</style>
<body>
<p class="test">Test passes if this text is green</p>
......
......@@ -6,7 +6,7 @@
<link rel="match" href="greentext-ref.html">
<meta name="assert" content="rgb() with 8-bit numbers and percent alpha, also no comma">
<style>
.test {color: rgb(0 80.0 0 / 100%)}
.test {color: rgb(0 128.0 0 / 100%)}
</style>
<body>
<p class="test">Test passes if this text is green</p>
......
......@@ -6,7 +6,7 @@
<link rel="match" href="greentext-ref.html">
<meta name="assert" content="legacy rgb() with 8-bit numbers and percent alpha, with commas">
<style>
.test {color: rgb(0, 80.0, 0, 100%)}
.test {color: rgb(0, 128.0, 0, 100%)}
</style>
<body>
<p class="test">Test passes if this text is green</p>
......
<!DOCTYPE html>
<meta charset="utf-8">
<title>CSS Color 4: RGB Channel Rounding</title>
<link rel="author" title="Chris Nardi" href="mailto:cnardi@chromium.org">
<link rel="help" href="https://drafts.csswg.org/css-color-4/#rgb-functions">
<meta name="assert" content="Tests if RGB channels indicated as decimals are rounded correctly">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<div id="test" style="color: rgb(45, 23, 27)"></div>
<script>
test(function() {
var element = document.getElementById('test');
color = "rgb(2.5, 3.4, 4.6)";
expected = ["rgb(3, 3, 5)", color];
element.style.cssText = "color: " + color;
assert_in_array(getComputedStyle(element).color, expected);
}, 'Tests that RGB channels are rounded appropriately');
</script>
......@@ -6,7 +6,7 @@
<link rel="match" href="greentext-ref.html">
<meta name="assert" content="legacy rgba() with 8-bit numbers and no alpha, also no comma">
<style>
.test {color: rgba(0 80.0 0)}
.test {color: rgba(0 128.0 0)}
</style>
<body>
<p class="test">Test passes if this text is green</p>
......
......@@ -6,7 +6,7 @@
<link rel="match" href="greentext-ref.html">
<meta name="assert" content="legacy rgba() with 8-bit numbers and numeric alpha, also no comma">
<style>
.test {color: rgba(0 80.0 0 / 1)}
.test {color: rgba(0 128.0 0 / 1)}
</style>
<body>
<p class="test">Test passes if this text is green</p>
......
......@@ -6,7 +6,7 @@
<link rel="match" href="greentext-ref.html">
<meta name="assert" content="legacy rgba() with 8-bit numbers and percent alpha, also no comma">
<style>
.test {color: rgba(0 80.0 0 / 100%)}
.test {color: rgba(0 128.0 0 / 100%)}
</style>
<body>
<p class="test">Test passes if this text is green</p>
......
......@@ -6,7 +6,7 @@
<link rel="match" href="greentext-ref.html">
<meta name="assert" content="legacy rgba() with 8-bit numbers and percent alpha, with commas">
<style>
.test {color: rgba(0, 80.0, 0, 100%)}
.test {color: rgba(0, 128.0, 0, 100%)}
</style>
<body>
<p class="test">Test passes if this text is green</p>
......
This is a testharness.js-based test.
FAIL OffscreenCanvas test: 2d.fillStyle.parse.css-color-4-rgb-1 assert_equals: Red channel of the pixel at (50, 25) expected 0 but got 255
Harness: the test ran to completion.
This is a testharness.js-based test.
FAIL OffscreenCanvas test: 2d.fillStyle.parse.css-color-4-rgba-1 assert_equals: Red channel of the pixel at (50, 25) expected 0 but got 255
Harness: the test ran to completion.
......@@ -24,14 +24,14 @@ var testScenarios = [
['Test Case 6', 'rgba(50.0%, 50%, a%, 1)', [102, 102, 102, 255]],
['Test Case 7', 'rgba(500%, 0%, 0%, 1)', [255, 0, 0, 255]],
['Test Case 8', 'rgba(100%, 100%, 100%, 1)', [255, 255, 255, 255]],
['Test Case 9', 'rgba(10.5%, 80%, 70%, 1)', [26, 204, 179, 255]],
['Test Case 9', 'rgba(10.5%, 80%, 70%, 1)', [27, 204, 179, 255]],
['Test Case 10', 'rgba(0%, 0%, 0%, 1)', [0, 0, 0, 255]],
['Test Case 11', 'rgba(50.0%, 50.0%, 50.0%, 1)', [128, 128, 128, 255]],
['Test Case 12', 'rgba(100%, 100%, 100%, 1)', [255, 255, 255, 255]],
['Test Case 13', 'rgba(10.5%, 80%, 70%, 1)', [26, 204, 179, 255]],
['Test Case 13', 'rgba(10.5%, 80%, 70%, 1)', [27, 204, 179, 255]],
['Test Case 14', 'rgba(55.5%, 0.5%, 110%, 1)', [142, 1, 255, 255]],
['Test Case 15', 'rgba(60.59998%, 0.59999%, 110.12345%, 1)', [155, 1, 255, 255]],
['Test Case 16', 'rgba(10.999%, 0.999%, 0.000009%, 1)', [28, 2, 0, 255]],
['Test Case 15', 'rgba(60.59998%, 0.59999%, 110.12345%, 1)', [155, 2, 255, 255]],
['Test Case 16', 'rgba(10.999%, 0.999%, 0.000009%, 1)', [28, 3, 0, 255]],
['Test Case 17', 'rgba(79.99999%, 99.99999%, 500%, 1)', [204, 255, 255, 255]],
['Test Case 18', 'rgba(0, 0, 0, -0.10)', [0, 0, 0, 0]],
['Test Case 19', 'rgba(0, 0, 0, -5.0)', [0, 0, 0, 0]],
......
layer at (0,0) size 800x600
LayoutView at (0,0) size 800x600
layer at (0,0) size 800x80
LayoutBlockFlow {HTML} at (0,0) size 800x80.17
LayoutBlockFlow {BODY} at (8,13.39) size 784x53.39 [color=#008000]
LayoutBlockFlow {H1} at (0,0) size 784x20 [color=#FF0000]
LayoutText {#text} at (0,0) size 580x20
text run at (0,0) width 580: "This should be red, not green"
LayoutBlockFlow {H1} at (0,33.39) size 784x20 [color=#000080]
LayoutText {#text} at (0,0) size 600x20
text run at (0,0) width 600: "This should be blue, not green"
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html lang="en">
<head profile="http://www.ietf.org/rfc/rfc2731.txt">
<title>CSS 2.1 Test Suite: RGB color syntax error handling</title>
<style type="text/css">
body {color: green;}
h1#floatnumnum {color: rgb(255.0, 0, 0);}
h1#numnumfloat {color: rgb(0, 0, 128.0);}
</style>
<!DOCTYPE HTML>
<head>
<title>CSS Color 4: Decimal RGB channel parsing</title>
<style type="text/css">
body {color: green; font: 10px/1 Ahem;}
h1#floatnumnum {color: rgb(255.0, 0, 0);}
h1#numnumfloat {color: rgb(0, 0, 128.0);}
</style>
<script src="/resources/ahem.js"></script>
</head>
<body>
<h1 id="floatnumnum">This should be green, not red</h1>
<h1 id="numnumfloat">This should be green, not blue</h1>
</body></html>
<h1 id="floatnumnum">This should be red, not green</h1>
<h1 id="numnumfloat">This should be blue, not green</h1>
</body>
......@@ -21,6 +21,8 @@ Testing: rgb(1 2 3 / 20%)
rgba(1, 2, 3, 0.2)
Testing: rgbA(1 2 3)
rgb(1, 2, 3)
Testing: rgba(1.5 2.6 3.1)
rgb(2, 3, 3)
Testing: hsl(1, 100%, 50%)
hsl(1, 100%, 50%)
Testing: hsl(1 100% 50%)
......@@ -52,6 +54,8 @@ Testing: rgb(1 2 3 / 20%)
rgba(1, 2, 3, 0)
Testing: rgbA(1 2 3)
rgba(1, 2, 3, 0)
Testing: rgba(1.5 2.6 3.1)
rgba(2, 3, 3, 0)
Testing: hsl(1, 100%, 50%)
hsla(1, 100%, 50%, 0)
Testing: hsl(1 100% 50%)
......@@ -93,6 +97,9 @@ hex
Testing: rgbA(1 2 3)
hsl
hex
Testing: rgba(1.5 2.6 3.1)
hsl
hex
Testing: hsl(1, 100%, 50%)
hex
rgb
......
......@@ -41,9 +41,9 @@
{string: '#ABCDEFAA', format: cf.HEXA}, {string: 'rgb(1, 2, 3)', format: cf.RGB},
{string: 'rgba(1, 2, 3, 0.2)', format: cf.RGB}, {string: 'rgb(1, 2, 3, 0.2)', format: cf.RGB},
{string: 'rgb(1 2 3 / 20%)', format: cf.RGB}, {string: 'rgbA(1 2 3)', format: cf.RGB},
{string: 'hsl(1, 100%, 50%)', format: cf.HSL}, {string: 'hsl(1 100% 50%)', format: cf.HSL},
{string: 'hsla(1, 100%, 50%, 0.2)', format: cf.HSLA}, {string: 'hsl(1 100% 50% / 20%)', format: cf.HSL},
{string: 'hsL(1deg 100% 50% / 20%)', format: cf.HSL}
{string: 'rgba(1.5 2.6 3.1)', format: cf.RGB}, {string: 'hsl(1, 100%, 50%)', format: cf.HSL},
{string: 'hsl(1 100% 50%)', format: cf.HSL}, {string: 'hsla(1, 100%, 50%, 0.2)', format: cf.HSLA},
{string: 'hsl(1 100% 50% / 20%)', format: cf.HSL}, {string: 'hsL(1deg 100% 50% / 20%)', format: cf.HSL}
];
TestRunner.addResult('--- Testing colorString()');
......
......@@ -150,6 +150,15 @@ color: rgb(1 1 1/ 1)
rgb: rgb(1, 1, 1)
hsl: hsl(0, 0%, 0%)
color: rgba(1.5 1.5 1.5)
simple: true
original: rgba(1.5 1.5 1.5)
hex: #020202
hexa: #020202ff
shorthexa: null
rgb: rgba(1.5 1.5 1.5)
hsl: hsl(0, 0%, 1%)
color: hsl(-120, 100%, 50%)
simple: true
original: hsl(-120, 100%, 50%)
......@@ -297,16 +306,12 @@ SUCCESS: parsed invalid color rgb(a,b,c) to null
SUCCESS: parsed invalid color rgb(a,b,c,d) to null
SUCCESS: parsed invalid color rgb(1,1,1.2) to null
SUCCESS: parsed invalid color rgba(0 0 0 1%) to null
SUCCESS: parsed invalid color rgba(0,0,0,) to null
SUCCESS: parsed invalid color rgba(0 0, 0) to null
SUCCESS: parsed invalid color rgba(1.0 1.0 1.0) to null
SUCCESS: parsed invalid color rgba(1 1 1 / ) to null
SUCCESS: parsed invalid color rgb(1 1 / 1) to null
......
......@@ -17,7 +17,7 @@
// Each of these are valid
'rgba(0,0,0,0.5)', 'rgb(0,0,0,50%)', 'rgb( 0 0 0 / 50% )', 'rgb(1 1 1/1)', 'rgb(1 1 1/ 1)',
'hsl(-120, 100%, 50%)', 'hsl(-120deg, 100%, 50%)',
'rgba(1.5 1.5 1.5)', 'hsl(-120, 100%, 50%)', 'hsl(-120deg, 100%, 50%)',
'hsl(-120, 200%, 200%)', // clipped to hsl(240,100%,100%)
'hsl(-120, -200%, -200%)', // clipped to hsl(240,100%,100%)
'hsla(-120, -200%, -200%, -5)', // clipped to hsla(0,0%,0%,0)
......@@ -31,8 +31,8 @@
var invalidColors = [
// An invalid color, eg a value for a shorthand like 'border' which can have a color
'none', '#00000', '#ggg', 'rgb(a,b,c)', 'rgb(a,b,c,d)', 'rgb(1,1,1.2)', 'rgba(0 0 0 1%)', 'rgba(0,0,0,)',
'rgba(0 0, 0)', 'rgba(1.0 1.0 1.0)', 'rgba(1 1 1 / )', 'rgb(1 1 / 1)', 'rgb(1 1/1)', 'hsl(0,0,0)', 'hsl(0%, 0%, 0%)',
'none', '#00000', '#ggg', 'rgb(a,b,c)', 'rgb(a,b,c,d)', 'rgba(0 0 0 1%)', 'rgba(0,0,0,)',
'rgba(0 0, 0)', 'rgba(1 1 1 / )', 'rgb(1 1 / 1)', 'rgb(1 1/1)', 'hsl(0,0,0)', 'hsl(0%, 0%, 0%)',
'hsla(0,,0,1)', 'hsl(0, 0%, 0)', 'hsl(a,b,c)', 'hsla(0,0,0,0)', 'hsla(0 0% 0% 0)', 'hsla(0 turn, 0, 0, 0)', 'hsla'
];
......
......@@ -15,7 +15,7 @@ layer at (0,0) size 800x340
LayoutBlockFlow {P} at (0,108) size 784x20 [color=#008000]
LayoutText {#text} at (0,0) size 132x19
text run at (0,0) width 132: "This should be green"
LayoutBlockFlow {P} at (0,144) size 784x20 [color=#008000]
LayoutBlockFlow {P} at (0,144) size 784x20 [color=#FF0000]
LayoutText {#text} at (0,0) size 132x19
text run at (0,0) width 132: "This should be green"
LayoutBlockFlow {P} at (0,180) size 784x20 [color=#008000]
......
......@@ -15,7 +15,7 @@ layer at (0,0) size 800x322
LayoutBlockFlow {P} at (0,102) size 784x18 [color=#008000]
LayoutText {#text} at (0,0) size 134x18
text run at (0,0) width 134: "This should be green"
LayoutBlockFlow {P} at (0,136) size 784x18 [color=#008000]
LayoutBlockFlow {P} at (0,136) size 784x18 [color=#FF0000]
LayoutText {#text} at (0,0) size 134x18
text run at (0,0) width 134: "This should be green"
LayoutBlockFlow {P} at (0,170) size 784x18 [color=#008000]
......
layer at (0,0) size 800x600
LayoutView at (0,0) size 800x600
layer at (0,0) size 800x138
LayoutBlockFlow {HTML} at (0,0) size 800x138.31
LayoutBlockFlow {BODY} at (8,21.44) size 784x95.44 [color=#008000]
LayoutBlockFlow {H1} at (0,0) size 784x37
LayoutText {#text} at (0,0) size 398x37
text run at (0,0) width 398: "This should be green, not red"
LayoutBlockFlow {H1} at (0,58.44) size 784x37
LayoutText {#text} at (0,0) size 411x37
text run at (0,0) width 411: "This should be green, not blue"
......@@ -15,7 +15,7 @@ layer at (0,0) size 800x340
LayoutBlockFlow {P} at (0,108) size 784x20 [color=#008000]
LayoutText {#text} at (0,0) size 124x19
text run at (0,0) width 124: "This should be green"
LayoutBlockFlow {P} at (0,144) size 784x20 [color=#008000]
LayoutBlockFlow {P} at (0,144) size 784x20 [color=#FF0000]
LayoutText {#text} at (0,0) size 124x19
text run at (0,0) width 124: "This should be green"
LayoutBlockFlow {P} at (0,180) size 784x20 [color=#008000]
......
layer at (0,0) size 800x600
LayoutView at (0,0) size 800x600
layer at (0,0) size 800x138
LayoutBlockFlow {HTML} at (0,0) size 800x138.31
LayoutBlockFlow {BODY} at (8,21.44) size 784x95.44 [color=#008000]
LayoutBlockFlow {H1} at (0,0) size 784x37
LayoutText {#text} at (0,0) size 397x36
text run at (0,0) width 397: "This should be green, not red"
LayoutBlockFlow {H1} at (0,58.44) size 784x37
LayoutText {#text} at (0,0) size 411x36
text run at (0,0) width 411: "This should be green, not blue"
......@@ -5,7 +5,7 @@ layer at (0,0) size 800x600
LayoutSVGHiddenContainer {defs} at (0,0) size 0x0
LayoutSVGResourceMasker {mask} [id="mask"] [maskUnits=objectBoundingBox] [maskContentUnits=userSpaceOnUse]
LayoutSVGRect {rect} at (0,0) size 140x140 [fill={[type=SOLID] [color=#00E600]}] [x=0.00] [y=0.00] [width=140.00] [height=140.00]
LayoutSVGEllipse {circle} at (30,30) size 80x80 [fill={[type=SOLID] [color=#00FD00]}] [cx=70.00] [cy=70.00] [r=40.00]
LayoutSVGEllipse {circle} at (30,30) size 80x80 [fill={[type=SOLID] [color=#00FC00]}] [cx=70.00] [cy=70.00] [r=40.00]
LayoutSVGRect {rect} at (20,20) size 100x100 [fill={[type=SOLID] [color=#000000]}] [x=20.00] [y=20.00] [width=100.00] [height=100.00]
[masker="mask"] LayoutSVGResourceMasker {mask} at (10,10) size 120x120
LayoutSVGRect {rect} at (150,20) size 100x100 [fill={[type=SOLID] [color=#6F6F6F]}] [x=150.00] [y=20.00] [width=100.00] [height=100.00]
......
......@@ -196,6 +196,7 @@ static inline bool IsColorPropertyID(CSSPropertyID property_id) {
template <typename CharacterType>
static int CheckForValidDouble(const CharacterType* string,
const CharacterType* end,
const bool terminated_by_space,
const char terminator) {
int length = end - string;
if (length < 1)
......@@ -205,7 +206,8 @@ static int CheckForValidDouble(const CharacterType* string,
int processed_length = 0;
for (int i = 0; i < length; ++i) {
if (string[i] == terminator) {
if (string[i] == terminator ||
(terminated_by_space && IsHTMLSpace<CharacterType>(string[i]))) {
processed_length = i;
break;
}
......@@ -229,8 +231,10 @@ template <typename CharacterType>
static int ParseDouble(const CharacterType* string,
const CharacterType* end,
const char terminator,
const bool terminated_by_space,
double& value) {
int length = CheckForValidDouble(string, end, terminator);
int length =
CheckForValidDouble(string, end, terminated_by_space, terminator);
if (!length)
return 0;
......@@ -264,13 +268,13 @@ static int ParseDouble(const CharacterType* string,
}
template <typename CharacterType>
static bool ParseColorIntOrPercentage(const CharacterType*& string,
const CharacterType* end,
const char terminator,
bool& should_whitespace_terminate,
bool is_first_value,
CSSPrimitiveValue::UnitType& expect,
int& value) {
static bool ParseColorNumberOrPercentage(const CharacterType*& string,
const CharacterType* end,
const char terminator,
bool& should_whitespace_terminate,
bool is_first_value,
CSSPrimitiveValue::UnitType& expect,
int& value) {
const CharacterType* current = string;
double local_value = 0;
bool negative = false;
......@@ -297,21 +301,29 @@ static bool ParseColorIntOrPercentage(const CharacterType*& string,
if (current == end)
return false;
if (expect == CSSPrimitiveValue::UnitType::kNumber &&
(*current == '.' || *current == '%'))
if (expect == CSSPrimitiveValue::UnitType::kNumber && *current == '%')
return false;
if (*current == '.') {
// We already parsed the integral part, try to parse
// the fraction part of the percentage value.
double percentage = 0;
int num_characters_parsed = ParseDouble(current, end, '%', percentage);
if (!num_characters_parsed)
return false;
current += num_characters_parsed;
if (*current != '%')
return false;
local_value += percentage;
// the fraction part.
double fractional = 0;
int num_characters_parsed =
ParseDouble(current, end, '%', false, fractional);
if (num_characters_parsed) {
// Number is a percent.
current += num_characters_parsed;
if (*current != '%')
return false;
} else {
// Number is a decimal.
num_characters_parsed =
ParseDouble(current, end, terminator, true, fractional);
if (!num_characters_parsed)
return false;
current += num_characters_parsed;
}
local_value += fractional;
}
if (expect == CSSPrimitiveValue::UnitType::kPercentage && *current != '%')
......@@ -319,7 +331,7 @@ static bool ParseColorIntOrPercentage(const CharacterType*& string,
if (*current == '%') {
expect = CSSPrimitiveValue::UnitType::kPercentage;
local_value = local_value / 100.0 * 256.0;
local_value = local_value / 100.0 * 255.0;
// Clamp values at 255 for percentages over 100%
if (local_value > 255)
local_value = 255;
......@@ -346,7 +358,7 @@ static bool ParseColorIntOrPercentage(const CharacterType*& string,
current++;
// Clamp negative values at zero.
value = negative ? 0 : static_cast<int>(local_value);
value = negative ? 0 : static_cast<int>(roundf(local_value));
string = current;
return true;
}
......@@ -390,7 +402,7 @@ static inline bool ParseAlphaValue(const CharacterType*& string,
return false;
if (string[0] != '0' && string[0] != '1' && string[0] != '.') {
if (CheckForValidDouble(string, end, terminator)) {
if (CheckForValidDouble(string, end, false, terminator)) {
value = negative ? 0 : 255;
string = end;
return true;
......@@ -415,7 +427,7 @@ static inline bool ParseAlphaValue(const CharacterType*& string,
}
double alpha = 0;
if (!ParseDouble(string, end, terminator, alpha))
if (!ParseDouble(string, end, terminator, false, alpha))
return false;
value =
negative ? 0 : static_cast<int>(roundf(std::min(alpha, 1.0) * 255.0f));
......@@ -464,25 +476,27 @@ static bool FastParseColorInternal(RGBA32& rgb,
bool should_whitespace_terminate = true;
bool no_whitespace_check = false;
if (!ParseColorIntOrPercentage(current, end, ',',
should_whitespace_terminate,
true /* is_first_value */, expect, red))
if (!ParseColorNumberOrPercentage(current, end, ',',
should_whitespace_terminate,
true /* is_first_value */, expect, red))
return false;
if (!ParseColorIntOrPercentage(current, end, ',',
should_whitespace_terminate,
false /* is_first_value */, expect, green))
if (!ParseColorNumberOrPercentage(
current, end, ',', should_whitespace_terminate,
false /* is_first_value */, expect, green))
return false;
if (!ParseColorIntOrPercentage(current, end, ',', no_whitespace_check,
false /* is_first_value */, expect, blue)) {
if (!ParseColorNumberOrPercentage(current, end, ',', no_whitespace_check,
false /* is_first_value */, expect,
blue)) {
// Might have slash as separator
if (ParseColorIntOrPercentage(current, end, '/', no_whitespace_check,
false /* is_first_value */, expect, blue)) {
if (ParseColorNumberOrPercentage(current, end, '/', no_whitespace_check,
false /* is_first_value */, expect,
blue)) {
if (!should_whitespace_terminate)
return false;
should_have_alpha = true;
}
// Might not have alpha
else if (!ParseColorIntOrPercentage(
else if (!ParseColorNumberOrPercentage(
current, end, ')', no_whitespace_check,
false /* is_first_value */, expect, blue))
return false;
......
......@@ -159,4 +159,30 @@ TEST(CSSParserFastPathsTest, ParseColorWithNewSyntax) {
EXPECT_EQ(nullptr, value);
}
TEST(CSSParserFastPathsTest, ParseColorWithDecimal) {
CSSValue* value = CSSParserFastPaths::ParseColor("rgba(0.0, 0.0, 0.0, 1.0)",
kHTMLStandardMode);
EXPECT_NE(nullptr, value);
EXPECT_TRUE(value->IsColorValue());
EXPECT_EQ(Color::kBlack, ToCSSColorValue(*value).Value());
value =
CSSParserFastPaths::ParseColor("rgb(0.0, 0.0, 0.0)", kHTMLStandardMode);
EXPECT_NE(nullptr, value);
EXPECT_TRUE(value->IsColorValue());
EXPECT_EQ(Color::kBlack, ToCSSColorValue(*value).Value());
value =
CSSParserFastPaths::ParseColor("rgb(0.0 , 0.0,0.0)", kHTMLStandardMode);
EXPECT_NE(nullptr, value);
EXPECT_TRUE(value->IsColorValue());
EXPECT_EQ(Color::kBlack, ToCSSColorValue(*value).Value());
value = CSSParserFastPaths::ParseColor("rgb(254.5, 254.5, 254.5)",
kHTMLStandardMode);
EXPECT_NE(nullptr, value);
EXPECT_TRUE(value->IsColorValue());
EXPECT_EQ(Color::kWhite, ToCSSColorValue(*value).Value());
}
} // namespace blink
......@@ -543,17 +543,16 @@ CSSURIValue* ConsumeUrl(CSSParserTokenRange& range,
static int ClampRGBComponent(const CSSPrimitiveValue& value) {
double result = value.GetDoubleValue();
// TODO(timloh): Multiply by 2.55 and round instead of floor.
if (value.IsPercentage())
result *= 2.56;
return clampTo<int>(result, 0, 255);
result *= 2.55;
return clampTo<int>(roundf(result), 0, 255);
}
static bool ParseRGBParameters(CSSParserTokenRange& range, RGBA32& result) {
DCHECK(range.Peek().FunctionId() == CSSValueRgb ||
range.Peek().FunctionId() == CSSValueRgba);
CSSParserTokenRange args = ConsumeFunction(range);
CSSPrimitiveValue* color_parameter = ConsumeInteger(args);
CSSPrimitiveValue* color_parameter = ConsumeNumber(args, kValueRangeAll);
if (!color_parameter)
color_parameter = ConsumePercent(args, kValueRangeAll);
if (!color_parameter)
......@@ -572,7 +571,7 @@ static bool ParseRGBParameters(CSSParserTokenRange& range, RGBA32& result) {
return false;
}
color_parameter = is_percent ? ConsumePercent(args, kValueRangeAll)
: ConsumeInteger(args);
: ConsumeNumber(args, kValueRangeAll);
if (!color_parameter)
return false;
color_array[i] = ClampRGBComponent(*color_parameter);
......
......@@ -175,15 +175,12 @@ Common.Color = class {
/**
* @param {string} value
* @param {boolean} onlyInteger
* return {number}
*/
static _parsePercentOrNumber(value, onlyInteger) {
static _parsePercentOrNumber(value) {
if (isNaN(value.replace('%', '')))
return null;
var parsed = parseFloat(value);
if (onlyInteger && (value.indexOf('.') !== -1 || !Number.isInteger(parsed)))
return null;
if (value.indexOf('%') !== -1) {
if (value.indexOf('%') !== value.length - 1)
......@@ -198,8 +195,7 @@ Common.Color = class {
* return {number}
*/
static _parseRgbNumeric(value) {
// Only integers are accepted for channels currently.
var parsed = Common.Color._parsePercentOrNumber(value, true);
var parsed = Common.Color._parsePercentOrNumber(value);
if (parsed === null)
return null;
......@@ -243,7 +239,7 @@ Common.Color = class {
* return {number}
*/
static _parseAlphaNumeric(value) {
return Common.Color._parsePercentOrNumber(value, false);
return Common.Color._parsePercentOrNumber(value);
}
/**
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment