Skip to content

Commit

Permalink
Support parsing angles with unitless zero exceptions
Browse files Browse the repository at this point in the history
Fixes #298
  • Loading branch information
devongovett committed Sep 20, 2022
1 parent 6e47cdb commit cbd392f
Show file tree
Hide file tree
Showing 5 changed files with 71 additions and 24 deletions.
11 changes: 11 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8710,6 +8710,8 @@ mod tests {
minify_test(".foo { transform: rotateX(405deg)}", ".foo{transform:rotateX(405deg)}");
minify_test(".foo { transform: rotateY(405deg)}", ".foo{transform:rotateY(405deg)}");
minify_test(".foo { transform: rotate(-200deg)}", ".foo{transform:rotate(-200deg)}");
minify_test(".foo { transform: rotate(0)", ".foo{transform:rotate(0)}");
minify_test(".foo { transform: rotate(0deg)", ".foo{transform:rotate(0)}");
minify_test(
".foo { transform: rotateX(-200deg)}",
".foo{transform:rotateX(-200deg)}",
Expand Down Expand Up @@ -8969,6 +8971,10 @@ mod tests {
".foo { background: linear-gradient(yellow, red 30%, red 40%, blue); }",
".foo{background:linear-gradient(#ff0,red 30% 40%,#00f)}",
);
minify_test(
".foo { background: linear-gradient(0, yellow, blue); }",
".foo{background:linear-gradient(#00f,#ff0)}",
);
minify_test(
".foo { background: -webkit-linear-gradient(yellow, blue) }",
".foo{background:-webkit-linear-gradient(#ff0,#00f)}",
Expand Down Expand Up @@ -9141,6 +9147,10 @@ mod tests {
".foo { background: conic-gradient(from 0deg, #f06, gold) }",
".foo{background:conic-gradient(#f06,gold)}",
);
minify_test(
".foo { background: conic-gradient(from 0, #f06, gold) }",
".foo{background:conic-gradient(#f06,gold)}",
);
minify_test(
".foo { background: conic-gradient(from 0deg at center, #f06, gold) }",
".foo{background:conic-gradient(#f06,gold)}",
Expand Down Expand Up @@ -20620,6 +20630,7 @@ mod tests {
".foo { filter: contrast(175%) brightness(3%); }",
".foo{filter:contrast(175%)brightness(3%)}",
);
minify_test(".foo { filter: hue-rotate(0) }", ".foo{filter:hue-rotate()}");

prefix_test(
".foo { filter: blur(5px) }",
Expand Down
3 changes: 2 additions & 1 deletion src/properties/effects.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,8 @@ impl<'i> Parse<'i> for Filter<'i> {
},
"hue-rotate" => {
input.parse_nested_block(|input| {
Ok(Filter::HueRotate(input.try_parse(Angle::parse).unwrap_or(Angle::Deg(0.0))))
// Spec has an exception for unitless zero angles: https://github.com/w3c/fxtf-drafts/issues/228
Ok(Filter::HueRotate(input.try_parse(Angle::parse_with_unitless_zero).unwrap_or(Angle::zero())))
})
},
"invert" => {
Expand Down
42 changes: 21 additions & 21 deletions src/properties/transform.rs
Original file line number Diff line number Diff line change
Expand Up @@ -948,19 +948,19 @@ impl<'i> Parse<'i> for Transform {
Ok(Transform::Scale3d(x, y, z))
},
"rotate" => {
let angle = Angle::parse(input)?;
let angle = Angle::parse_with_unitless_zero(input)?;
Ok(Transform::Rotate(angle))
},
"rotatex" => {
let angle = Angle::parse(input)?;
let angle = Angle::parse_with_unitless_zero(input)?;
Ok(Transform::RotateX(angle))
},
"rotatey" => {
let angle = Angle::parse(input)?;
let angle = Angle::parse_with_unitless_zero(input)?;
Ok(Transform::RotateY(angle))
},
"rotatez" => {
let angle = Angle::parse(input)?;
let angle = Angle::parse_with_unitless_zero(input)?;
Ok(Transform::RotateZ(angle))
},
"rotate3d" => {
Expand All @@ -970,24 +970,24 @@ impl<'i> Parse<'i> for Transform {
input.expect_comma()?;
let z = f32::parse(input)?;
input.expect_comma()?;
let angle = Angle::parse(input)?;
let angle = Angle::parse_with_unitless_zero(input)?;
Ok(Transform::Rotate3d(x, y, z, angle))
},
"skew" => {
let x = Angle::parse(input)?;
let x = Angle::parse_with_unitless_zero(input)?;
if input.try_parse(|input| input.expect_comma()).is_ok() {
let y = Angle::parse(input)?;
let y = Angle::parse_with_unitless_zero(input)?;
Ok(Transform::Skew(x, y))
} else {
Ok(Transform::Skew(x, Angle::Deg(0.0)))
}
},
"skewx" => {
let angle = Angle::parse(input)?;
let angle = Angle::parse_with_unitless_zero(input)?;
Ok(Transform::SkewX(angle))
},
"skewy" => {
let angle = Angle::parse(input)?;
let angle = Angle::parse_with_unitless_zero(input)?;
Ok(Transform::SkewY(angle))
},
"perspective" => {
Expand Down Expand Up @@ -1135,37 +1135,37 @@ impl ToCss for Transform {
}
Rotate(angle) => {
dest.write_str("rotate(")?;
angle.to_css(dest)?;
angle.to_css_with_unitless_zero(dest)?;
dest.write_char(')')
}
RotateX(angle) => {
dest.write_str("rotateX(")?;
angle.to_css(dest)?;
angle.to_css_with_unitless_zero(dest)?;
dest.write_char(')')
}
RotateY(angle) => {
dest.write_str("rotateY(")?;
angle.to_css(dest)?;
angle.to_css_with_unitless_zero(dest)?;
dest.write_char(')')
}
RotateZ(angle) => {
dest.write_str(if dest.minify { "rotate(" } else { "rotateZ(" })?;
angle.to_css(dest)?;
angle.to_css_with_unitless_zero(dest)?;
dest.write_char(')')
}
Rotate3d(x, y, z, angle) => {
if dest.minify && *x == 1.0 && *y == 0.0 && *z == 0.0 {
// rotate3d(1, 0, 0, a) => rotateX(a)
dest.write_str("rotateX(")?;
angle.to_css(dest)?;
angle.to_css_with_unitless_zero(dest)?;
} else if dest.minify && *x == 0.0 && *y == 1.0 && *z == 0.0 {
// rotate3d(0, 1, 0, a) => rotateY(a)
dest.write_str("rotateY(")?;
angle.to_css(dest)?;
angle.to_css_with_unitless_zero(dest)?;
} else if dest.minify && *x == 0.0 && *y == 0.0 && *z == 1.0 {
// rotate3d(0, 0, 1, a) => rotate(a)
dest.write_str("rotate(")?;
angle.to_css(dest)?;
angle.to_css_with_unitless_zero(dest)?;
} else {
dest.write_str("rotate3d(")?;
x.to_css(dest)?;
Expand All @@ -1174,32 +1174,32 @@ impl ToCss for Transform {
dest.delim(',', false)?;
z.to_css(dest)?;
dest.delim(',', false)?;
angle.to_css(dest)?;
angle.to_css_with_unitless_zero(dest)?;
}
dest.write_char(')')
}
Skew(x, y) => {
if dest.minify && x.is_zero() && !y.is_zero() {
dest.write_str("skewY(")?;
y.to_css(dest)?
y.to_css_with_unitless_zero(dest)?
} else {
dest.write_str("skew(")?;
x.to_css(dest)?;
if !y.is_zero() {
dest.delim(',', false)?;
y.to_css(dest)?;
y.to_css_with_unitless_zero(dest)?;
}
}
dest.write_char(')')
}
SkewX(angle) => {
dest.write_str(if dest.minify { "skew(" } else { "skewX(" })?;
angle.to_css(dest)?;
angle.to_css_with_unitless_zero(dest)?;
dest.write_char(')')
}
SkewY(angle) => {
dest.write_str("skewY(")?;
angle.to_css(dest)?;
angle.to_css_with_unitless_zero(dest)?;
dest.write_char(')')
}
Perspective(len) => {
Expand Down
31 changes: 31 additions & 0 deletions src/values/angle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,22 @@ pub enum Angle {

impl<'i> Parse<'i> for Angle {
fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i, ParserError<'i>>> {
Self::parse_internal(input, false)
}
}

impl Angle {
/// Parses an angle, allowing unitless zero values.
pub fn parse_with_unitless_zero<'i, 't>(
input: &mut Parser<'i, 't>,
) -> Result<Self, ParseError<'i, ParserError<'i>>> {
Self::parse_internal(input, true)
}

fn parse_internal<'i, 't>(
input: &mut Parser<'i, 't>,
allow_unitless_zero: bool,
) -> Result<Self, ParseError<'i, ParserError<'i>>> {
match input.try_parse(Calc::parse) {
Ok(Calc::Value(v)) => return Ok(*v),
// Angles are always compatible, so they will always compute to a value.
Expand All @@ -56,6 +72,7 @@ impl<'i> Parse<'i> for Angle {
_ => return Err(location.new_unexpected_token_error(token.clone())),
}
}
Token::Number { value, .. } if value == 0.0 && allow_unitless_zero => Ok(Angle::zero()),
ref token => return Err(location.new_unexpected_token_error(token.clone())),
}
}
Expand Down Expand Up @@ -86,6 +103,20 @@ impl ToCss for Angle {
}
}

impl Angle {
/// Prints the angle, allowing unitless zero values.
pub fn to_css_with_unitless_zero<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError>
where
W: std::fmt::Write,
{
if self.is_zero() {
(0.0).to_css(dest)
} else {
self.to_css(dest)
}
}
}

impl Angle {
/// Returns the angle in radians.
pub fn to_radians(&self) -> CSSNumber {
Expand Down
8 changes: 6 additions & 2 deletions src/values/gradient.rs
Original file line number Diff line number Diff line change
Expand Up @@ -388,7 +388,9 @@ impl LineDirection {
input: &mut Parser<'i, 't>,
is_prefixed: bool,
) -> Result<Self, ParseError<'i, ParserError<'i>>> {
if let Ok(angle) = input.try_parse(Angle::parse) {
// Spec allows unitless zero angles for gradients.
// https://w3c.github.io/csswg-drafts/css-images-3/#linear-gradient-syntax
if let Ok(angle) = input.try_parse(Angle::parse_with_unitless_zero) {
return Ok(LineDirection::Angle(angle));
}

Expand Down Expand Up @@ -672,7 +674,9 @@ impl ConicGradient {
fn parse<'i, 't>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i, ParserError<'i>>> {
let angle = input.try_parse(|input| {
input.expect_ident_matching("from")?;
Angle::parse(input)
// Spec allows unitless zero angles for gradients.
// https://w3c.github.io/csswg-drafts/css-images-4/#valdef-conic-gradient-angle
Angle::parse_with_unitless_zero(input)
});

let position = input.try_parse(|input| {
Expand Down

0 comments on commit cbd392f

Please sign in to comment.