Skip to content

Commit 15d44ab

Browse files
color: Support CSS Color Level 4 rgb & hsl syntax (servo#113)
1 parent 4a84ac1 commit 15d44ab

File tree

4 files changed

+11873
-34
lines changed

4 files changed

+11873
-34
lines changed

src/color.rs

+83-19
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use std::cmp;
66
use std::fmt;
7+
use std::f64::consts::PI;
78

89
use super::{Token, Parser, ToCss};
910
use tokenizer::NumericValue;
@@ -398,47 +399,84 @@ fn clamp_f32(val: f32) -> u8 {
398399

399400
#[inline]
400401
fn parse_color_function(name: &str, arguments: &mut Parser) -> Result<Color, ()> {
401-
let (is_rgb, has_alpha) = match_ignore_ascii_case! { name,
402-
"rgba" => (true, true),
403-
"rgb" => (true, false),
404-
"hsl" => (false, false),
405-
"hsla" => (false, true),
402+
let is_rgb = match_ignore_ascii_case! { name,
403+
"rgba" => true,
404+
"rgb" => true,
405+
"hsl" => false,
406+
"hsla" => false,
406407
_ => return Err(())
407408
};
408409

409410
let red: u8;
410411
let green: u8;
411412
let blue: u8;
413+
let mut uses_commas = false;
412414
if is_rgb {
413415
// Either integers or percentages, but all the same type.
414416
// https://drafts.csswg.org/css-color/#rgb-functions
415417
match try!(arguments.next()) {
416-
Token::Number(NumericValue { int_value: Some(v), .. }) => {
417-
red = clamp_i32(v);
418-
try!(arguments.expect_comma());
419-
green = clamp_i32(try!(arguments.expect_integer()));
420-
try!(arguments.expect_comma());
421-
blue = clamp_i32(try!(arguments.expect_integer()));
418+
Token::Number(NumericValue { value: v, .. }) => {
419+
red = clamp_i32(v as i32);
420+
green = clamp_i32(match try!(arguments.next()) {
421+
Token::Number(NumericValue { value: v, .. }) => v,
422+
Token::Comma => {
423+
uses_commas = true;
424+
try!(arguments.expect_number())
425+
}
426+
_ => return Err(())
427+
} as i32);
428+
if uses_commas {
429+
try!(arguments.expect_comma());
430+
}
431+
blue = clamp_i32(try!(arguments.expect_number()) as i32);
422432
}
423433
Token::Percentage(ref v) => {
424434
red = clamp_f32(v.unit_value);
425-
try!(arguments.expect_comma());
426-
green = clamp_f32(try!(arguments.expect_percentage()));
427-
try!(arguments.expect_comma());
435+
green = clamp_f32(match try!(arguments.next()) {
436+
Token::Percentage(ref v) => v.unit_value,
437+
Token::Comma => {
438+
uses_commas = true;
439+
try!(arguments.expect_percentage())
440+
}
441+
_ => return Err(())
442+
});
443+
if uses_commas {
444+
try!(arguments.expect_comma());
445+
}
428446
blue = clamp_f32(try!(arguments.expect_percentage()));
429447
}
430448
_ => return Err(())
431449
};
432450
} else {
433-
let hue_degrees = try!(arguments.expect_number());
451+
let hue_degrees = match try!(arguments.next()) {
452+
Token::Number(NumericValue { value: v, .. }) => v,
453+
Token::Dimension(NumericValue { value: v, .. }, unit) => {
454+
match &*unit {
455+
"deg" => v,
456+
"grad" => v * 360. / 400.,
457+
"rad" => v * 360. / (2. * PI as f32),
458+
"turn" => v * 360.,
459+
_ => return Err(())
460+
}
461+
}
462+
_ => return Err(())
463+
};
434464
// Subtract an integer before rounding, to avoid some rounding errors:
435465
let hue_normalized_degrees = hue_degrees - 360. * (hue_degrees / 360.).floor();
436466
let hue = hue_normalized_degrees / 360.;
437467
// Saturation and lightness are clamped to 0% ... 100%
438468
// https://drafts.csswg.org/css-color/#the-hsl-notation
439-
try!(arguments.expect_comma());
440-
let saturation = try!(arguments.expect_percentage()).max(0.).min(1.);
441-
try!(arguments.expect_comma());
469+
let saturation = match try!(arguments.next()) {
470+
Token::Percentage(ref v) => v.unit_value,
471+
Token::Comma => {
472+
uses_commas = true;
473+
try!(arguments.expect_percentage())
474+
}
475+
_ => return Err(())
476+
}.max(0.).min(1.);
477+
if uses_commas {
478+
try!(arguments.expect_comma());
479+
}
442480
let lightness = try!(arguments.expect_percentage()).max(0.).min(1.);
443481

444482
// https://drafts.csswg.org/css-color/#hsl-color
@@ -461,12 +499,38 @@ fn parse_color_function(name: &str, arguments: &mut Parser) -> Result<Color, ()>
461499
blue = clamp_f32(hue_to_rgb(m1, m2, hue_times_3 - 1.));
462500
}
463501

502+
let alpha = if !arguments.is_exhausted() {
503+
if uses_commas {
504+
try!(arguments.expect_comma());
505+
} else {
506+
match try!(arguments.next()) {
507+
Token::Delim('/') => {},
508+
_ => return Err(())
509+
};
510+
};
511+
let token = try!(arguments.next());
512+
match token {
513+
Token::Number(NumericValue { value: v, .. }) => {
514+
clamp_f32(v)
515+
}
516+
Token::Percentage(ref v) => {
517+
clamp_f32(v.unit_value)
518+
}
519+
_ => {
520+
return Err(())
521+
}
522+
}
523+
} else {
524+
255
525+
};
526+
527+
/*
464528
let alpha = if has_alpha {
465529
try!(arguments.expect_comma());
466530
clamp_f32(try!(arguments.expect_number()))
467531
} else {
468532
255
469-
};
533+
};*/
470534
try!(arguments.expect_exhausted());
471535
rgba(red, green, blue, alpha)
472536
}

src/css-parsing-tests/color3.json

+120-11
Original file line numberDiff line numberDiff line change
@@ -65,13 +65,14 @@
6565
"rgb()", null,
6666
"rgb(0)", null,
6767
"rgb(0, 0)", null,
68-
"rgb(0, 0, 0, 0)", null,
68+
"rgb(0, 0, 0, 0)", [0, 0, 0, 0],
6969
"rgb(0%)", null,
7070
"rgb(0%, 0%)", null,
71-
"rgb(0%, 0%, 0%, 0%)", null,
72-
"rgb(0%, 0%, 0%, 0)", null,
71+
"rgb(0%, 0%, 0%, 0%)", [0, 0, 0, 0],
72+
"rgb(0%, 0%, 0%, 0)", [0, 0, 0, 0],
7373

7474
"rgba(0, 0, 0, 0)", [0, 0, 0, 0],
75+
"rgba(.3, -1.4, -0.001e2, 0)", [0, 0, 0, 0],
7576
"rgba(204, 0, 102, 0.25)", [204, 0, 102, 64],
7677
"RGBA(255,255,255, 0)", [255, 255, 255, 0],
7778
"rgBA(0, 51,255, 1)", [0, 51, 255, 255],
@@ -92,20 +93,20 @@
9293
"rgba(0%, 20%, 100%, -0.1)", [0, 51, 255, 0],
9394
"rgba(0%, 20%, 100%, -139)", [0, 51, 255, 0],
9495

95-
"rgba(255,255,255, 0%)", null,
96+
"rgba(255,255,255, 0%)", [255, 255, 255, 0],
9697
"rgba(10%, 50%, 0, 1)", null,
9798
"rgba(255, 50%, 0%, 1)", null,
9899
"rgba(0, 0, 0 0)", null,
99100
"rgba(0, 0, 0, 0deg)", null,
100101
"rgba(0, 0, 0, light)", null,
101102
"rgba()", null,
102103
"rgba(0)", null,
103-
"rgba(0, 0, 0)", null,
104+
"rgba(0, 0, 0)", [0, 0, 0, 255],
104105
"rgba(0, 0, 0, 0, 0)", null,
105106
"rgba(0%)", null,
106107
"rgba(0%, 0%)", null,
107-
"rgba(0%, 0%, 0%)", null,
108-
"rgba(0%, 0%, 0%, 0%)", null,
108+
"rgba(0%, 0%, 0%)", [0, 0, 0, 255],
109+
"rgba(0%, 0%, 0%, 0%)", [0, 0, 0, 0],
109110
"rgba(0%, 0%, 0%, 0%, 0%)", null,
110111

111112
"HSL(0, 0%, 0%)", [0, 0, 0, 255],
@@ -118,12 +119,12 @@
118119
"hsl(10, 50%, 0)", null,
119120
"hsl(50%, 50%, 0%)", null,
120121
"hsl(0, 0% 0%)", null,
121-
"hsl(30deg, 100%, 100%)", null,
122+
"hsl(30deg, 100%, 100%)", [255, 255, 255, 255],
122123
"hsl(0, 0%, light)", null,
123124
"hsl()", null,
124125
"hsl(0)", null,
125126
"hsl(0, 0%)", null,
126-
"hsl(0, 0%, 0%, 0%)", null,
127+
"hsl(0, 0%, 0%, 0%)", [0, 0, 0, 0],
127128

128129
"HSLA(-300, 100%, 37.5%, 1)", [192, 192, 0, 255],
129130
"hsLA(-300, 100%, 37.5%, 12)", [192, 192, 0, 255],
@@ -134,13 +135,121 @@
134135
"hsla(10, 50%, 0, 1)", null,
135136
"hsla(50%, 50%, 0%, 1)", null,
136137
"hsla(0, 0% 0%, 1)", null,
137-
"hsla(30deg, 100%, 100%, 1)", null,
138+
"hsla(30deg, 100%, 100%, 1)", [255, 255, 255, 255],
138139
"hsla(0, 0%, light, 1)", null,
139140
"hsla()", null,
140141
"hsla(0)", null,
141142
"hsla(0, 0%)", null,
142-
"hsla(0, 0%, 0%, 50%)", null,
143+
"hsla(0, 0%, 0%, 50%)", [0, 0, 0, 128],
143144
"hsla(0, 0%, 0%, 255, 0%)", null,
144145

146+
"rgb(0 0 0 0)", null,
147+
"rgb(0 0 0 / 0)", [0, 0, 0, 0],
148+
"rgb(0%)", null,
149+
"rgb(0% 0%)", null,
150+
"rgb(0% 0% 0% / 0%)", [0, 0, 0, 0],
151+
"rgb(0% 0% 0% / 0)", [0, 0, 0, 0],
152+
153+
"rgba(0%)", null,
154+
"rgba(0% 0%)", null,
155+
"rgba(0% 0% 0%)", [0, 0, 0, 255],
156+
"rgba(0% 0% 0% / 0%)", [0, 0, 0, 0],
157+
"rgba(0% 0% 0% / 0% 0%)", null,
158+
159+
"rgb(0, 0 0 0)", null,
160+
"rgb(0 0, 0 0)", null,
161+
"rgb(0 0 0, 0)", null,
162+
"rgb(0, 0, 0 0)", null,
163+
164+
"rgba(0%, 0% 0%)", null,
165+
"rgba(0% 0% 0%, 0%)", null,
166+
167+
"HSL(0 0% 0%)", [0, 0, 0, 255],
168+
"hsL(0 100% 50%)", [255, 0, 0, 255],
169+
"hsl(60 100% 37.5%)", [192, 192, 0, 255],
170+
171+
"HSLA(-300 100% 37.5% /1)", [192, 192, 0, 255],
172+
"hsLA(-300 100% 37.5% /12)", [192, 192, 0, 255],
173+
174+
"hsl(0, 0 0 0)", null,
175+
"hsl(0 0, 0 0)", null,
176+
"hsl(0 0 0, 0)", null,
177+
"hsl(0, 0, 0 0)", null,
178+
179+
"hsla(0%, 0% 0%)", null,
180+
"hsla(0% 0% 0%, 0%)", null,
181+
182+
"hsla(120.0, 75%, 50%, 20%)", [32, 224, 32, 51],
183+
"hsla(120, 75%, 50%, 0.4)", [32, 224, 32, 102],
184+
"hsla(120 75% 50% / 60%)", [32, 224, 32, 153],
185+
"hsla(120.0 75% 50% / 1.0)", [32, 224, 32, 255],
186+
"hsla(120/* comment */75%/* comment */50%/1.0)", [32, 224, 32, 255],
187+
"hsla(120,/* comment */75%,/* comment */50%,100%)", [32, 224, 32, 255],
188+
"hsla(120.0, 75%, 50%)", [32, 224, 32, 255],
189+
"hsla(120 75% 50%)", [32, 224, 32, 255],
190+
"hsla(120/* comment */75%/* comment */50%)", [32, 224, 32, 255],
191+
"hsla(120/* comment */,75%,/* comment */50%)", [32, 224, 32, 255],
192+
"hsl(120, 75%, 50%, 0.2)", [32, 224, 32, 51],
193+
"hsl(120, 75%, 50%, 40%)", [32, 224, 32, 102],
194+
"hsl(120 75% 50% / 0.6)", [32, 224, 32, 153],
195+
"hsl(120 75% 50% / 80%)", [32, 224, 32, 204],
196+
"hsl(120/* comment */75%/* comment */50%/1.0)", [32, 224, 32, 255],
197+
"hsl(120/* comment */75%/* comment */50%/100%)", [32, 224, 32, 255],
198+
"hsl(120,/* comment */75%,/* comment */50%,1.0)", [32, 224, 32, 255],
199+
"hsl(120,/* comment */75%,/* comment */50%,100%)", [32, 224, 32, 255],
200+
"hsl(120/* comment */75%/* comment */50%)", [32, 224, 32, 255],
201+
"hsl(120/* comment */,75%,/* comment */50%)", [32, 224, 32, 255],
202+
"hsla(120, 75%, 50%, 0.2)", [32, 224, 32, 51],
203+
"hsl(240, 75%, 50%)", [32, 32, 224, 255],
204+
"hsla(120, 75%, 50%)", [32, 224, 32, 255],
205+
"hsla(120.0, 75%, 50%)", [32, 224, 32, 255],
206+
"hsla(1.2e2, 75%, 50%)", [32, 224, 32, 255],
207+
"hsla(1.2E2, 75%, 50%)", [32, 224, 32, 255],
208+
"hsla(60, 75%, 50%)", [224, 224, 32, 255],
209+
"hsl(120, 75%, 50%, 0.2)", [32, 224, 32, 51],
210+
"hsl(120.0, 75%, 50%, 0.4)", [32, 224, 32, 102],
211+
"hsl(1.2e2, 75%, 50%, 0.6)", [32, 224, 32, 153],
212+
"hsl(1.2E2, 75%, 50%, 0.8)", [32, 224, 32, 204],
213+
"hsl(60.0, 75%, 50%, 1.0)", [224, 224, 32, 255],
214+
"rgb(10%, 60%, 10%, 20%)", [25, 153, 25, 51],
215+
"rgb(10, 175, 10, 0.4)", [10, 175, 10, 102],
216+
"rgb(10 175 10 / 60%)", [10, 175, 10, 153],
217+
"rgb(10.0 175.0 10.0 / 0.8)", [10, 175, 10, 204],
218+
"rgb(10/* comment */175/* comment */10/100%)", [10, 175, 10, 255],
219+
"rgb(10,/* comment */150,/* comment */50)", [10, 150, 50, 255],
220+
"rgb(10%, 60%, 10%)", [25, 153, 25, 255],
221+
"rgb(10.0 100.0 100.0)", [10, 100, 100, 255],
222+
"rgb(10/* comment */75/* comment */125)", [10, 75, 125, 255],
223+
"rgb(10.0, 50.0, 150.0)", [10, 50, 150, 255],
224+
"rgba(10.0, 175.0, 10.0, 0.2)", [10, 175, 10, 51],
225+
"rgba(10, 175, 10, 40%)", [10, 175, 10, 102],
226+
"rgba(10% 75% 10% / 0.6)", [25, 192, 25, 153],
227+
"rgba(10 175 10 / 80%)", [10, 175, 10, 204],
228+
"rgba(10/* comment */175/* comment */10/100%)", [10, 175, 10, 255],
229+
"rgba(10,/* comment */150,/* comment */50)", [10, 150, 50, 255],
230+
"rgba(10.0, 125.0, 75.0)", [10, 125, 75, 255],
231+
"rgba(10%, 45%, 45%)", [25, 115, 115, 255],
232+
"rgba(10/* comment */75/* comment */125)", [10, 75, 125, 255],
233+
"rgba(10.0, 50.0, 150.0)", [10, 50, 150, 255],
234+
"rgb(10, 175, 10, 0.2)", [10, 175, 10, 51],
235+
"rgb(10, 175, 10, 0.4)", [10, 175, 10, 102],
236+
"rgb(10, 175, 10, 0.6)", [10, 175, 10, 153],
237+
"rgb(10%, 70%, 10%, 0.8)", [25, 179, 25, 204],
238+
"rgb(10%, 70%, 10%, 1.0)", [25, 179, 25, 255],
239+
"rgba(10, 150, 50)", [10, 150, 50, 255],
240+
"rgba(10, 125, 75)", [10, 125, 75, 255],
241+
"rgba(10%,40%, 40%)", [25, 102, 102, 255],
242+
"rgba(10%, 45%, 50%)", [25, 115, 128, 255],
243+
"rgba(10%, 50%, 60%)", [25, 128, 153, 255],
244+
245+
"hsla(120deg, 75%, 50%, 0.4)", [32, 224, 32, 102],
246+
"hsla(133.33333333grad, 75%, 50%, 0.6)", [32, 224, 32, 153],
247+
"hsla(2.0943951024rad, 75%, 50%, 0.8)", [32, 224, 32, 204],
248+
"hsla(0.3333333333turn, 75%, 50%, 1.0)", [32, 224, 32, 255],
249+
"hsl(600deg, 75%, 50%)", [32, 32, 224, 255],
250+
"hsl(1066.66666666grad, 75%, 50%)", [32, 32, 224, 255],
251+
"hsl(10.4719755118rad, 75%, 50%)", [32, 32, 224, 255],
252+
"hsl(2.6666666666turn, 75%, 50%)", [32, 32, 224, 255],
253+
145254
"cmyk(0, 0, 0, 0)", null
146255
]

0 commit comments

Comments
 (0)