diff --git a/src/browser/tests/element/css_style_properties.html b/src/browser/tests/element/css_style_properties.html index 6cb0e0bdc..3592df67e 100644 --- a/src/browser/tests/element/css_style_properties.html +++ b/src/browser/tests/element/css_style_properties.html @@ -159,6 +159,35 @@
+ +
+ diff --git a/src/browser/webapi/css/CSSStyleDeclaration.zig b/src/browser/webapi/css/CSSStyleDeclaration.zig index 12833c412..f1c915506 100644 --- a/src/browser/webapi/css/CSSStyleDeclaration.zig +++ b/src/browser/webapi/css/CSSStyleDeclaration.zig @@ -245,7 +245,9 @@ fn normalizePropertyName(name: []const u8, buf: []u8) []const u8 { } // Normalize CSS property values for canonical serialization -fn normalizePropertyValue(arena: Allocator, property_name: []const u8, value: []const u8) ![]const u8 { +fn normalizePropertyValue(arena: Allocator, property_name: []const u8, raw_value: []const u8) ![]const u8 { + const value = try normalizeLeadingZeros(arena, raw_value); + // Per CSSOM spec, unitless zero in length properties should serialize as "0px" if (std.mem.eql(u8, value, "0") and isLengthProperty(property_name)) { return "0px"; @@ -286,6 +288,51 @@ fn normalizePropertyValue(arena: Allocator, property_name: []const u8, value: [] return value; } +// Insert a leading "0" before any bare ".". A value might have multiple +// such bare digits +fn normalizeLeadingZeros(arena: Allocator, value: []const u8) ![]const u8 { + // A bare "." needs at least 2 chars, and a trailing "." can never be + // followed by a digit. So we only ever scan up to value.len - 1, which lets + // us index value[i + 1] without a bounds check. + if (value.len < 2) { + return value; + } + + var inserts: usize = 0; + for (value[0 .. value.len - 1], 0..) |c, i| { + if (c != '.') { + continue; + } + if (!std.ascii.isDigit(value[i + 1])) { + // next value isn't a digit + continue; + } + if (i > 0 and std.ascii.isDigit(value[i - 1])) { + // previous value is a digit + continue; + } + inserts += 1; + } + if (inserts == 0) { + return value; + } + + const buf = try arena.alloc(u8, value.len + inserts); + var w: usize = 0; + for (value[0 .. value.len - 1], 0..) |c, i| { + if (c == '.' and std.ascii.isDigit(value[i + 1]) and (i == 0 or !std.ascii.isDigit(value[i - 1]))) { + buf[w] = '0'; + w += 1; + } + buf[w] = c; + w += 1; + } + // The last char is copied unconditionally: it can't start a bare ".". + buf[w] = value[value.len - 1]; + w += 1; + return buf[0..w]; +} + // Canonicalize anchor-size() so that the dashed ident (anchor name) comes before the size keyword. // e.g. "anchor-size(width --foo)" -> "anchor-size(--foo width)" fn canonicalizeAnchorSize(arena: Allocator, value: []const u8, start_index: usize) ![]const u8 {