Avoid parsing to non-finite floating point numbers
In #165, this input 3E833 is parsed as a Token::Number whose value is std::f32::INFINITY, which serializes as inf.
We should definitely not serialize any number as inf (maybe an arbitrary large number instead), but probably also avoid non-finite numbers in tokens in the first place.
CC https://github.com/servo/servo/issues/15729
FWIW, in librsvg I ended up with this:
pub trait CssParserExt {
fn expect_finite_number(&mut self) -> Result<f32, ValueErrorKind>;
}
impl<'i, 't> CssParserExt for Parser<'i, 't> {
fn expect_finite_number(&mut self) -> Result<f32, ValueErrorKind> {
finite_f32(self.expect_number()?)
}
}
pub fn finite_f32(n: f32) -> Result<f32, ValueErrorKind> {
if n.is_finite() {
Ok(n)
} else {
Err(ValueErrorKind::Value("expected finite number".to_string()))
}
}
Of course this is tied to librsvg's error enums. The code uses expect_finite_number everywhere instead of cssparser's stock expect_number.
I guess the question is, should encountering a non-finite number just yield an UnexpectedToken error? I don't know the details of the CSS spec well enough :)
https://drafts.csswg.org/css-values/#numeric-ranges
CSS theoretically supports infinite precision and infinite ranges for all value types; however in reality implementations have finite capacity. UAs should support reasonably useful ranges and precisions.
Unfortunately I couldn’t easily find spec text that says what to do with inputs that exceed that chosen capacity.
I think we should parse it, and clamp it to some appropriate range. We should probably just not serialize inf. Gecko doesn't serialize inf, maybe this bug was fixed?
Probably when we started using the dtoa crate:
fn main() {
dbg!(dtoa_s(std::f32::INFINITY));
dbg!(dtoa_s(std::f32::NAN));
dbg!(dtoa_s(std::f32::MAX));
}
fn dtoa_s(x: f32) -> String {
let mut v = Vec::<u8>::new();
dtoa::write(&mut v, x).unwrap();
String::from_utf8(v).unwrap()
}
Playground, output:
[src/main.rs:2] dtoa_s(std::f32::INFINITY) = "3.4028237e38"
[src/main.rs:3] dtoa_s(std::f32::NAN) = "5.1042355e38"
[src/main.rs:4] dtoa_s(std::f32::MAX) = "3.4028235e38"
If the value is outside the allowed range, then unless otherwise specified, the declaration is invalid and must be ignored.
So, "too large" -> "parse error" sounds reasonable.
But section 10.9 has an interesting note:
Note: By definition, ±∞ are outside the allowed range for any property, and will clamp to the minimum/maximum value allowed.
So clamping to $large_value sounds fine too! If that makes number values rountrippable, this sounds good.
Parse errors seems like it might not be web-compatible at least for z-index where some pages use ever-larger value