Encode UTF8 non breakingspace (194, 160) as   - same as chrome

This commit is contained in:
Karl Seguin
2025-10-15 17:33:33 +08:00
parent 73da04bea2
commit b610aa1c0c
2 changed files with 25 additions and 3 deletions

View File

@@ -236,10 +236,10 @@ fn isVoid(elem: *parser.Element) !bool {
};
}
fn writeEscapedTextNode(writer: anytype, value: []const u8) !void {
fn writeEscapedTextNode(writer: *std.Io.Writer, value: []const u8) !void {
var v = value;
while (v.len > 0) {
const index = std.mem.indexOfAnyPos(u8, v, 0, &.{ '&', '<', '>' }) orelse {
const index = std.mem.indexOfAnyPos(u8, v, 0, &.{ '&', '<', '>', 194 }) orelse {
return writer.writeAll(v);
};
try writer.writeAll(v[0..index]);
@@ -247,13 +247,22 @@ fn writeEscapedTextNode(writer: anytype, value: []const u8) !void {
'&' => try writer.writeAll("&amp;"),
'<' => try writer.writeAll("&lt;"),
'>' => try writer.writeAll("&gt;"),
194 => {
// non breaking space
if (v.len > index + 1 and v[index + 1] == 160) {
try writer.writeAll("&nbsp;");
v = v[index + 2 ..];
continue;
}
try writer.writeByte(194);
},
else => unreachable,
}
v = v[index + 1 ..];
}
}
fn writeEscapedAttributeValue(writer: anytype, value: []const u8) !void {
fn writeEscapedAttributeValue(writer: *std.Io.Writer, value: []const u8) !void {
var v = value;
while (v.len > 0) {
const index = std.mem.indexOfAnyPos(u8, v, 0, &.{ '&', '<', '>', '"' }) orelse {

View File

@@ -326,3 +326,16 @@
testing.expectEqual("after begin", newElement.innerText);
testing.expectEqual("afterbegin", newElement.className);
</script>
<script id=nonBreakingSpace>
// Test non-breaking space encoding (critical for React hydration)
const div = document.createElement('div');
div.innerHTML = 'hello\xa0world';
testing.expectEqual('hello\xa0world', div.textContent);
testing.expectEqual('hello&nbsp;world', div.innerHTML);
// Test that outerHTML also encodes non-breaking spaces correctly
const p = document.createElement('p');
p.textContent = 'XAnge\xa0Privacy';
testing.expectEqual('<p>XAnge&nbsp;Privacy</p>', p.outerHTML);
</script>