Skip to content

Commit 20c9612

Browse files
committed
Do not crash on currentColor in color-mix or relative colors
Fixes parcel-bundler#522
1 parent cd7b432 commit 20c9612

File tree

6 files changed

+73
-51
lines changed

6 files changed

+73
-51
lines changed

examples/custom_at_rule.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,7 @@ impl<'a, 'i> Visitor<'i, AtRule> for ApplyVisitor<'a, 'i> {
223223
}
224224

225225
fn visit_color(&mut self, color: &mut lightningcss::values::color::CssColor) -> Result<(), Self::Error> {
226-
*color = color.to_lab();
226+
*color = color.to_lab().unwrap();
227227
Ok(())
228228
}
229229

src/lib.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16202,6 +16202,10 @@ mod tests {
1620216202
test("lch(from indianred l sin(c) h)", "lch(53.9252% .84797 26.8448)");
1620316203
test("lch(from indianred l sqrt(c) h)", "lch(53.9252% 7.16084 26.8448)");
1620416204
test("lch(from indianred l c sin(h))", "lch(53.9252% 51.2776 .990043)");
16205+
minify_test(
16206+
".foo{color:lch(from currentColor l c sin(h))}",
16207+
".foo{color:lch(from currentColor l c sin(h))}",
16208+
);
1620516209

1620616210
// The following tests were converted from WPT: https://github.com/web-platform-tests/wpt/blob/master/css/css-color/parsing/relative-color-valid.html
1620716211
// Find: test_valid_value\(`color`, `(.*?)`,\s*`(.*?)`\)
@@ -18053,6 +18057,14 @@ mod tests {
1805318057
..Default::default()
1805418058
},
1805518059
);
18060+
minify_test(
18061+
".foo { color: color-mix(in srgb, currentColor, blue); }",
18062+
".foo{color:color-mix(in srgb,currentColor,blue)}",
18063+
);
18064+
minify_test(
18065+
".foo { color: color-mix(in srgb, blue, currentColor); }",
18066+
".foo{color:color-mix(in srgb,blue,currentColor)}",
18067+
);
1805618068

1805718069
// regex for converting web platform tests:
1805818070
// test_computed_value\(.*?, `(.*?)`, `(.*?)`\);
@@ -18140,7 +18152,7 @@ mod tests {
1814018152

1814118153
let mut input = ParserInput::new(s);
1814218154
let mut parser = Parser::new(&mut input);
18143-
let v = CssColor::parse(&mut parser).unwrap().to_rgb();
18155+
let v = CssColor::parse(&mut parser).unwrap().to_rgb().unwrap();
1814418156
format!(".foo{{color:{}}}", v.to_css_string(PrinterOptions::default()).unwrap())
1814518157
}
1814618158

src/properties/box_shadow.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@ impl BoxShadowHandler {
207207
let rgb = box_shadows
208208
.iter()
209209
.map(|shadow| BoxShadow {
210-
color: shadow.color.to_rgb(),
210+
color: shadow.color.to_rgb().unwrap(),
211211
..shadow.clone()
212212
})
213213
.collect();
@@ -224,7 +224,7 @@ impl BoxShadowHandler {
224224
let p3 = box_shadows
225225
.iter()
226226
.map(|shadow| BoxShadow {
227-
color: shadow.color.to_p3(),
227+
color: shadow.color.to_p3().unwrap(),
228228
..shadow.clone()
229229
})
230230
.collect();
@@ -235,7 +235,7 @@ impl BoxShadowHandler {
235235
let lab = box_shadows
236236
.iter()
237237
.map(|shadow| BoxShadow {
238-
color: shadow.color.to_lab(),
238+
color: shadow.color.to_lab().unwrap(),
239239
..shadow.clone()
240240
})
241241
.collect();

src/properties/text.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1564,7 +1564,7 @@ impl FallbackValues for SmallVec<[TextShadow; 1]> {
15641564
let rgb = self
15651565
.iter()
15661566
.map(|shadow| TextShadow {
1567-
color: shadow.color.to_rgb(),
1567+
color: shadow.color.to_rgb().unwrap(),
15681568
..shadow.clone()
15691569
})
15701570
.collect();
@@ -1575,7 +1575,7 @@ impl FallbackValues for SmallVec<[TextShadow; 1]> {
15751575
let p3 = self
15761576
.iter()
15771577
.map(|shadow| TextShadow {
1578-
color: shadow.color.to_p3(),
1578+
color: shadow.color.to_p3().unwrap(),
15791579
..shadow.clone()
15801580
})
15811581
.collect();
@@ -1584,7 +1584,7 @@ impl FallbackValues for SmallVec<[TextShadow; 1]> {
15841584

15851585
if fallbacks.contains(ColorFallbackKind::LAB) {
15861586
for shadow in self.iter_mut() {
1587-
shadow.color = shadow.color.to_lab();
1587+
shadow.color = shadow.color.to_lab().unwrap();
15881588
}
15891589
}
15901590

src/values/color.rs

Lines changed: 52 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -287,18 +287,18 @@ impl CssColor {
287287
}
288288

289289
/// Converts the color to RGBA.
290-
pub fn to_rgb(&self) -> CssColor {
291-
RGBA::from(self).into()
290+
pub fn to_rgb(&self) -> Result<CssColor, ()> {
291+
Ok(RGBA::try_from(self)?.into())
292292
}
293293

294294
/// Converts the color to the LAB color space.
295-
pub fn to_lab(&self) -> CssColor {
296-
LAB::from(self).into()
295+
pub fn to_lab(&self) -> Result<CssColor, ()> {
296+
Ok(LAB::try_from(self)?.into())
297297
}
298298

299299
/// Converts the color to the P3 color space.
300-
pub fn to_p3(&self) -> CssColor {
301-
P3::from(self).into()
300+
pub fn to_p3(&self) -> Result<CssColor, ()> {
301+
Ok(P3::try_from(self)?.into())
302302
}
303303

304304
pub(crate) fn get_possible_fallbacks(&self, targets: Targets) -> ColorFallbackKind {
@@ -376,9 +376,9 @@ impl CssColor {
376376
}
377377

378378
match kind {
379-
ColorFallbackKind::RGB => self.to_rgb(),
380-
ColorFallbackKind::P3 => self.to_p3(),
381-
ColorFallbackKind::LAB => self.to_lab(),
379+
ColorFallbackKind::RGB => self.to_rgb().unwrap(),
380+
ColorFallbackKind::P3 => self.to_p3().unwrap(),
381+
ColorFallbackKind::LAB => self.to_lab().unwrap(),
382382
_ => unreachable!(),
383383
}
384384
}
@@ -406,15 +406,15 @@ impl FallbackValues for CssColor {
406406

407407
let mut res = Vec::new();
408408
if fallbacks.contains(ColorFallbackKind::RGB) {
409-
res.push(self.to_rgb());
409+
res.push(self.to_rgb().unwrap());
410410
}
411411

412412
if fallbacks.contains(ColorFallbackKind::P3) {
413-
res.push(self.to_p3());
413+
res.push(self.to_p3().unwrap());
414414
}
415415

416416
if fallbacks.contains(ColorFallbackKind::LAB) {
417-
*self = self.to_lab();
417+
*self = self.to_lab().unwrap();
418418
}
419419

420420
res
@@ -744,12 +744,14 @@ impl ComponentParser {
744744
Self { allow_none, from: None }
745745
}
746746

747-
fn parse_relative<'i, 't, T: From<CssColor> + ColorSpace>(
747+
fn parse_relative<'i, 't, T: TryFrom<CssColor> + ColorSpace>(
748748
&mut self,
749749
input: &mut Parser<'i, 't>,
750750
) -> Result<(), ParseError<'i, ParserError<'i>>> {
751751
if input.try_parse(|input| input.expect_ident_matching("from")).is_ok() {
752-
let from = T::from(CssColor::parse(input)?).resolve();
752+
let from = T::try_from(CssColor::parse(input)?)
753+
.map_err(|_| input.new_custom_error(ParserError::InvalidValue))?
754+
.resolve();
753755
self.from = Some(RelativeComponentParser::new(&from));
754756
}
755757

@@ -895,7 +897,7 @@ fn parse_color_function<'i, 't>(input: &mut Parser<'i, 't>) -> Result<CssColor,
895897

896898
/// Parses the lab() and oklab() functions.
897899
#[inline]
898-
fn parse_lab<'i, 't, T: From<CssColor> + ColorSpace>(
900+
fn parse_lab<'i, 't, T: TryFrom<CssColor> + ColorSpace>(
899901
input: &mut Parser<'i, 't>,
900902
parser: &mut ComponentParser,
901903
) -> Result<(f32, f32, f32, f32), ParseError<'i, ParserError<'i>>> {
@@ -917,7 +919,7 @@ fn parse_lab<'i, 't, T: From<CssColor> + ColorSpace>(
917919

918920
/// Parses the lch() and oklch() functions.
919921
#[inline]
920-
fn parse_lch<'i, 't, T: From<CssColor> + ColorSpace>(
922+
fn parse_lch<'i, 't, T: TryFrom<CssColor> + ColorSpace>(
921923
input: &mut Parser<'i, 't>,
922924
parser: &mut ComponentParser,
923925
) -> Result<(f32, f32, f32, f32), ParseError<'i, ParserError<'i>>> {
@@ -961,15 +963,16 @@ fn parse_predefined<'i, 't>(
961963
let colorspace = input.expect_ident_cloned()?;
962964

963965
if let Some(from) = &from {
966+
let handle_error = |_| input.new_custom_error(ParserError::InvalidValue);
964967
parser.from = Some(match_ignore_ascii_case! { &*&colorspace,
965-
"srgb" => RelativeComponentParser::new(&SRGB::from(from).resolve_missing()),
966-
"srgb-linear" => RelativeComponentParser::new(&SRGBLinear::from(from).resolve_missing()),
967-
"display-p3" => RelativeComponentParser::new(&P3::from(from).resolve_missing()),
968-
"a98-rgb" => RelativeComponentParser::new(&A98::from(from).resolve_missing()),
969-
"prophoto-rgb" => RelativeComponentParser::new(&ProPhoto::from(from).resolve_missing()),
970-
"rec2020" => RelativeComponentParser::new(&Rec2020::from(from).resolve_missing()),
971-
"xyz-d50" => RelativeComponentParser::new(&XYZd50::from(from).resolve_missing()),
972-
"xyz" | "xyz-d65" => RelativeComponentParser::new(&XYZd65::from(from).resolve_missing()),
968+
"srgb" => RelativeComponentParser::new(&SRGB::try_from(from).map_err(handle_error)?.resolve_missing()),
969+
"srgb-linear" => RelativeComponentParser::new(&SRGBLinear::try_from(from).map_err(handle_error)?.resolve_missing()),
970+
"display-p3" => RelativeComponentParser::new(&P3::try_from(from).map_err(handle_error)?.resolve_missing()),
971+
"a98-rgb" => RelativeComponentParser::new(&A98::try_from(from).map_err(handle_error)?.resolve_missing()),
972+
"prophoto-rgb" => RelativeComponentParser::new(&ProPhoto::try_from(from).map_err(handle_error)?.resolve_missing()),
973+
"rec2020" => RelativeComponentParser::new(&Rec2020::try_from(from).map_err(handle_error)?.resolve_missing()),
974+
"xyz-d50" => RelativeComponentParser::new(&XYZd50::try_from(from).map_err(handle_error)?.resolve_missing()),
975+
"xyz" | "xyz-d65" => RelativeComponentParser::new(&XYZd65::try_from(from).map_err(handle_error)?.resolve_missing()),
973976
_ => return Err(location.new_unexpected_token_error(
974977
cssparser::Token::Ident(colorspace.clone())
975978
))
@@ -1013,7 +1016,7 @@ fn parse_predefined<'i, 't>(
10131016
/// Only the modern syntax with no commas is handled here, cssparser handles the legacy syntax.
10141017
/// The results of this function are stored as floating point if there are any `none` components.
10151018
#[inline]
1016-
fn parse_hsl_hwb<'i, 't, T: From<CssColor> + ColorSpace>(
1019+
fn parse_hsl_hwb<'i, 't, T: TryFrom<CssColor> + ColorSpace>(
10171020
input: &mut Parser<'i, 't>,
10181021
parser: &mut ComponentParser,
10191022
) -> Result<(f32, f32, f32, f32), ParseError<'i, ParserError<'i>>> {
@@ -2575,27 +2578,29 @@ macro_rules! color_space {
25752578
}
25762579
}
25772580

2578-
impl From<&CssColor> for $space {
2579-
fn from(color: &CssColor) -> $space {
2580-
match color {
2581+
impl TryFrom<&CssColor> for $space {
2582+
type Error = ();
2583+
fn try_from(color: &CssColor) -> Result<$space, ()> {
2584+
Ok(match color {
25812585
CssColor::RGBA(rgba) => (*rgba).into(),
25822586
CssColor::LAB(lab) => (**lab).into(),
25832587
CssColor::Predefined(predefined) => (**predefined).into(),
25842588
CssColor::Float(float) => (**float).into(),
2585-
CssColor::CurrentColor => unreachable!(),
2586-
}
2589+
CssColor::CurrentColor => return Err(()),
2590+
})
25872591
}
25882592
}
25892593

2590-
impl From<CssColor> for $space {
2591-
fn from(color: CssColor) -> $space {
2592-
match color {
2594+
impl TryFrom<CssColor> for $space {
2595+
type Error = ();
2596+
fn try_from(color: CssColor) -> Result<$space, ()> {
2597+
Ok(match color {
25932598
CssColor::RGBA(rgba) => rgba.into(),
25942599
CssColor::LAB(lab) => (*lab).into(),
25952600
CssColor::Predefined(predefined) => (*predefined).into(),
25962601
CssColor::Float(float) => (*float).into(),
2597-
CssColor::CurrentColor => unreachable!(),
2598-
}
2602+
CssColor::CurrentColor => return Err(()),
2603+
})
25992604
}
26002605
}
26012606
};
@@ -2879,7 +2884,7 @@ fn parse_color_mix<'i, 't>(input: &mut Parser<'i, 't>) -> Result<CssColor, Parse
28792884
return Err(input.new_custom_error(ParserError::InvalidValue));
28802885
}
28812886

2882-
Ok(match method {
2887+
match method {
28832888
ColorSpaceName::SRGB => first_color.interpolate::<SRGB>(p1, &second_color, p2, hue_method),
28842889
ColorSpaceName::SRGBLinear => first_color.interpolate::<SRGBLinear>(p1, &second_color, p2, hue_method),
28852890
ColorSpaceName::Hsl => first_color.interpolate::<HSL>(p1, &second_color, p2, hue_method),
@@ -2892,7 +2897,8 @@ fn parse_color_mix<'i, 't>(input: &mut Parser<'i, 't>) -> Result<CssColor, Parse
28922897
first_color.interpolate::<XYZd65>(p1, &second_color, p2, hue_method)
28932898
}
28942899
ColorSpaceName::XYZd50 => first_color.interpolate::<XYZd50>(p1, &second_color, p2, hue_method),
2895-
})
2900+
}
2901+
.map_err(|_| input.new_custom_error(ParserError::InvalidValue))
28962902
}
28972903

28982904
impl CssColor {
@@ -2932,10 +2938,10 @@ impl CssColor {
29322938
other: &'a CssColor,
29332939
mut p2: f32,
29342940
method: HueInterpolationMethod,
2935-
) -> CssColor
2941+
) -> Result<CssColor, ()>
29362942
where
29372943
T: 'static
2938-
+ From<&'a CssColor>
2944+
+ TryFrom<&'a CssColor>
29392945
+ Interpolate
29402946
+ Into<CssColor>
29412947
+ Into<OKLCH>
@@ -2944,13 +2950,17 @@ impl CssColor {
29442950
+ From<OKLCH>
29452951
+ Copy,
29462952
{
2953+
if matches!(self, CssColor::CurrentColor) || matches!(other, CssColor::CurrentColor) {
2954+
return Err(());
2955+
}
2956+
29472957
let type_id = TypeId::of::<T>();
29482958
let converted_first = self.get_type_id() != type_id;
29492959
let converted_second = other.get_type_id() != type_id;
29502960

29512961
// https://drafts.csswg.org/css-color-5/#color-mix-result
2952-
let mut first_color = T::from(self);
2953-
let mut second_color = T::from(other);
2962+
let mut first_color = T::try_from(self).map_err(|_| ())?;
2963+
let mut second_color = T::try_from(other).map_err(|_| ())?;
29542964

29552965
if converted_first && !first_color.in_gamut() {
29562966
first_color = map_gamut(first_color);
@@ -2993,7 +3003,7 @@ impl CssColor {
29933003
let mut result_color = first_color.interpolate(p1, &second_color, p2);
29943004
result_color.unpremultiply(alpha_multiplier);
29953005

2996-
result_color.into()
3006+
Ok(result_color.into())
29973007
}
29983008
}
29993009

src/values/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
//! };
1919
//!
2020
//! let color = CssColor::parse_string("lch(50% 75 0)").unwrap();
21-
//! let rgb = color.to_rgb();
21+
//! let rgb = color.to_rgb().unwrap();
2222
//! assert_eq!(rgb.to_css_string(PrinterOptions::default()).unwrap(), "#e1157b");
2323
//! ```
2424
//!

0 commit comments

Comments
 (0)