Skip to content

Commit

Permalink
Do not crash on currentColor in color-mix or relative colors
Browse files Browse the repository at this point in the history
Fixes #522
  • Loading branch information
devongovett committed Jun 25, 2023
1 parent cd7b432 commit 20c9612
Show file tree
Hide file tree
Showing 6 changed files with 73 additions and 51 deletions.
2 changes: 1 addition & 1 deletion examples/custom_at_rule.rs
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ impl<'a, 'i> Visitor<'i, AtRule> for ApplyVisitor<'a, 'i> {
}

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

Expand Down
14 changes: 13 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16202,6 +16202,10 @@ mod tests {
test("lch(from indianred l sin(c) h)", "lch(53.9252% .84797 26.8448)");
test("lch(from indianred l sqrt(c) h)", "lch(53.9252% 7.16084 26.8448)");
test("lch(from indianred l c sin(h))", "lch(53.9252% 51.2776 .990043)");
minify_test(
".foo{color:lch(from currentColor l c sin(h))}",
".foo{color:lch(from currentColor l c sin(h))}",
);

// The following tests were converted from WPT: https://github.com/web-platform-tests/wpt/blob/master/css/css-color/parsing/relative-color-valid.html
// Find: test_valid_value\(`color`, `(.*?)`,\s*`(.*?)`\)
Expand Down Expand Up @@ -18053,6 +18057,14 @@ mod tests {
..Default::default()
},
);
minify_test(
".foo { color: color-mix(in srgb, currentColor, blue); }",
".foo{color:color-mix(in srgb,currentColor,blue)}",
);
minify_test(
".foo { color: color-mix(in srgb, blue, currentColor); }",
".foo{color:color-mix(in srgb,blue,currentColor)}",
);

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

let mut input = ParserInput::new(s);
let mut parser = Parser::new(&mut input);
let v = CssColor::parse(&mut parser).unwrap().to_rgb();
let v = CssColor::parse(&mut parser).unwrap().to_rgb().unwrap();
format!(".foo{{color:{}}}", v.to_css_string(PrinterOptions::default()).unwrap())
}

Expand Down
6 changes: 3 additions & 3 deletions src/properties/box_shadow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ impl BoxShadowHandler {
let rgb = box_shadows
.iter()
.map(|shadow| BoxShadow {
color: shadow.color.to_rgb(),
color: shadow.color.to_rgb().unwrap(),
..shadow.clone()
})
.collect();
Expand All @@ -224,7 +224,7 @@ impl BoxShadowHandler {
let p3 = box_shadows
.iter()
.map(|shadow| BoxShadow {
color: shadow.color.to_p3(),
color: shadow.color.to_p3().unwrap(),
..shadow.clone()
})
.collect();
Expand All @@ -235,7 +235,7 @@ impl BoxShadowHandler {
let lab = box_shadows
.iter()
.map(|shadow| BoxShadow {
color: shadow.color.to_lab(),
color: shadow.color.to_lab().unwrap(),
..shadow.clone()
})
.collect();
Expand Down
6 changes: 3 additions & 3 deletions src/properties/text.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1564,7 +1564,7 @@ impl FallbackValues for SmallVec<[TextShadow; 1]> {
let rgb = self
.iter()
.map(|shadow| TextShadow {
color: shadow.color.to_rgb(),
color: shadow.color.to_rgb().unwrap(),
..shadow.clone()
})
.collect();
Expand All @@ -1575,7 +1575,7 @@ impl FallbackValues for SmallVec<[TextShadow; 1]> {
let p3 = self
.iter()
.map(|shadow| TextShadow {
color: shadow.color.to_p3(),
color: shadow.color.to_p3().unwrap(),
..shadow.clone()
})
.collect();
Expand All @@ -1584,7 +1584,7 @@ impl FallbackValues for SmallVec<[TextShadow; 1]> {

if fallbacks.contains(ColorFallbackKind::LAB) {
for shadow in self.iter_mut() {
shadow.color = shadow.color.to_lab();
shadow.color = shadow.color.to_lab().unwrap();
}
}

Expand Down
94 changes: 52 additions & 42 deletions src/values/color.rs
Original file line number Diff line number Diff line change
Expand Up @@ -287,18 +287,18 @@ impl CssColor {
}

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

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

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

pub(crate) fn get_possible_fallbacks(&self, targets: Targets) -> ColorFallbackKind {
Expand Down Expand Up @@ -376,9 +376,9 @@ impl CssColor {
}

match kind {
ColorFallbackKind::RGB => self.to_rgb(),
ColorFallbackKind::P3 => self.to_p3(),
ColorFallbackKind::LAB => self.to_lab(),
ColorFallbackKind::RGB => self.to_rgb().unwrap(),
ColorFallbackKind::P3 => self.to_p3().unwrap(),
ColorFallbackKind::LAB => self.to_lab().unwrap(),
_ => unreachable!(),
}
}
Expand Down Expand Up @@ -406,15 +406,15 @@ impl FallbackValues for CssColor {

let mut res = Vec::new();
if fallbacks.contains(ColorFallbackKind::RGB) {
res.push(self.to_rgb());
res.push(self.to_rgb().unwrap());
}

if fallbacks.contains(ColorFallbackKind::P3) {
res.push(self.to_p3());
res.push(self.to_p3().unwrap());
}

if fallbacks.contains(ColorFallbackKind::LAB) {
*self = self.to_lab();
*self = self.to_lab().unwrap();
}

res
Expand Down Expand Up @@ -744,12 +744,14 @@ impl ComponentParser {
Self { allow_none, from: None }
}

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

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

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

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

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

impl From<&CssColor> for $space {
fn from(color: &CssColor) -> $space {
match color {
impl TryFrom<&CssColor> for $space {
type Error = ();
fn try_from(color: &CssColor) -> Result<$space, ()> {
Ok(match color {
CssColor::RGBA(rgba) => (*rgba).into(),
CssColor::LAB(lab) => (**lab).into(),
CssColor::Predefined(predefined) => (**predefined).into(),
CssColor::Float(float) => (**float).into(),
CssColor::CurrentColor => unreachable!(),
}
CssColor::CurrentColor => return Err(()),
})
}
}

impl From<CssColor> for $space {
fn from(color: CssColor) -> $space {
match color {
impl TryFrom<CssColor> for $space {
type Error = ();
fn try_from(color: CssColor) -> Result<$space, ()> {
Ok(match color {
CssColor::RGBA(rgba) => rgba.into(),
CssColor::LAB(lab) => (*lab).into(),
CssColor::Predefined(predefined) => (*predefined).into(),
CssColor::Float(float) => (*float).into(),
CssColor::CurrentColor => unreachable!(),
}
CssColor::CurrentColor => return Err(()),
})
}
}
};
Expand Down Expand Up @@ -2879,7 +2884,7 @@ fn parse_color_mix<'i, 't>(input: &mut Parser<'i, 't>) -> Result<CssColor, Parse
return Err(input.new_custom_error(ParserError::InvalidValue));
}

Ok(match method {
match method {
ColorSpaceName::SRGB => first_color.interpolate::<SRGB>(p1, &second_color, p2, hue_method),
ColorSpaceName::SRGBLinear => first_color.interpolate::<SRGBLinear>(p1, &second_color, p2, hue_method),
ColorSpaceName::Hsl => first_color.interpolate::<HSL>(p1, &second_color, p2, hue_method),
Expand All @@ -2892,7 +2897,8 @@ fn parse_color_mix<'i, 't>(input: &mut Parser<'i, 't>) -> Result<CssColor, Parse
first_color.interpolate::<XYZd65>(p1, &second_color, p2, hue_method)
}
ColorSpaceName::XYZd50 => first_color.interpolate::<XYZd50>(p1, &second_color, p2, hue_method),
})
}
.map_err(|_| input.new_custom_error(ParserError::InvalidValue))
}

impl CssColor {
Expand Down Expand Up @@ -2932,10 +2938,10 @@ impl CssColor {
other: &'a CssColor,
mut p2: f32,
method: HueInterpolationMethod,
) -> CssColor
) -> Result<CssColor, ()>
where
T: 'static
+ From<&'a CssColor>
+ TryFrom<&'a CssColor>
+ Interpolate
+ Into<CssColor>
+ Into<OKLCH>
Expand All @@ -2944,13 +2950,17 @@ impl CssColor {
+ From<OKLCH>
+ Copy,
{
if matches!(self, CssColor::CurrentColor) || matches!(other, CssColor::CurrentColor) {
return Err(());
}

let type_id = TypeId::of::<T>();
let converted_first = self.get_type_id() != type_id;
let converted_second = other.get_type_id() != type_id;

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

if converted_first && !first_color.in_gamut() {
first_color = map_gamut(first_color);
Expand Down Expand Up @@ -2993,7 +3003,7 @@ impl CssColor {
let mut result_color = first_color.interpolate(p1, &second_color, p2);
result_color.unpremultiply(alpha_multiplier);

result_color.into()
Ok(result_color.into())
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/values/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
//! };
//!
//! let color = CssColor::parse_string("lch(50% 75 0)").unwrap();
//! let rgb = color.to_rgb();
//! let rgb = color.to_rgb().unwrap();
//! assert_eq!(rgb.to_css_string(PrinterOptions::default()).unwrap(), "#e1157b");
//! ```
//!
Expand Down

0 comments on commit 20c9612

Please sign in to comment.