Skip to content

Dimension with an e-prefixed unit serializes to a number token #439

Description

@hey-jj

A Token::Dimension whose unit begins with e or E followed by a digit serializes to text that re-parses as a Number with a different value. The unit is written straight after the numeric value with no separation, so a value of 1 with unit e5 becomes 1e5, which is scientific notation for the number 100000.

Reproducer

cssparser 0.37.0, default features.

use cssparser::{Parser, ParserInput, ToCss, Token};

fn main() {
    let mut pi = ParserInput::new("1\\65 5");
    let mut p = Parser::new(&mut pi);
    let t = p.next().unwrap().clone();
    // Dimension { value: 1.0, int_value: Some(1), unit: "e5" }
    let s = t.to_css_string();
    assert_eq!(s, "1e5");

    let mut pi2 = ParserInput::new(&s);
    let mut p2 = Parser::new(&mut pi2);
    let t2 = p2.next().unwrap().clone();
    // Number { value: 100000.0 }
    assert!(matches!(t2, Token::Number { .. }));
}

Observed vs expected

Observed: the dimension serializes to 1e5. Re-parsing 1e5 yields Number { value: 100000.0 }, so the unit is gone and the value is scaled by 100000. With unit e90 the input serializes to 1e90, which re-parses to Number { value: inf }.

Expected: the serialized form re-parses to the same dimension. CSS Syntax Level 3 requires a serialized token to round-trip. The serializer already guards a unit that would collide with an identifier, so the same guard fits a unit that could read as an exponent. Writing the unit with a leading escape, as in 1\65 5, blocks the exponent reading.

Root cause

src/serializer.rs:107. The dimension arm writes the value and then the unit with no check for a unit that starts with e or E and a digit.

Scope

Any consumer that serializes tokens and later re-parses them can read a wrong numeric value, or an infinity where the source held a finite dimension. The value token is also reachable by constructing a Token::Dimension directly, without parsing. Present on main as of the latest commit.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions