Skip to content

Commit b610aa1

Browse files
committed
Encode UTF8 non breakingspace (194, 160) as   - same as chrome
1 parent 73da04b commit b610aa1

File tree

2 files changed

+25
-3
lines changed

2 files changed

+25
-3
lines changed

src/browser/dump.zig

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -236,24 +236,33 @@ fn isVoid(elem: *parser.Element) !bool {
236236
};
237237
}
238238

239-
fn writeEscapedTextNode(writer: anytype, value: []const u8) !void {
239+
fn writeEscapedTextNode(writer: *std.Io.Writer, value: []const u8) !void {
240240
var v = value;
241241
while (v.len > 0) {
242-
const index = std.mem.indexOfAnyPos(u8, v, 0, &.{ '&', '<', '>' }) orelse {
242+
const index = std.mem.indexOfAnyPos(u8, v, 0, &.{ '&', '<', '>', 194 }) orelse {
243243
return writer.writeAll(v);
244244
};
245245
try writer.writeAll(v[0..index]);
246246
switch (v[index]) {
247247
'&' => try writer.writeAll("&amp;"),
248248
'<' => try writer.writeAll("&lt;"),
249249
'>' => try writer.writeAll("&gt;"),
250+
194 => {
251+
// non breaking space
252+
if (v.len > index + 1 and v[index + 1] == 160) {
253+
try writer.writeAll("&nbsp;");
254+
v = v[index + 2 ..];
255+
continue;
256+
}
257+
try writer.writeByte(194);
258+
},
250259
else => unreachable,
251260
}
252261
v = v[index + 1 ..];
253262
}
254263
}
255264

256-
fn writeEscapedAttributeValue(writer: anytype, value: []const u8) !void {
265+
fn writeEscapedAttributeValue(writer: *std.Io.Writer, value: []const u8) !void {
257266
var v = value;
258267
while (v.len > 0) {
259268
const index = std.mem.indexOfAnyPos(u8, v, 0, &.{ '&', '<', '>', '"' }) orelse {

src/tests/dom/element.html

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,3 +326,16 @@
326326
testing.expectEqual("after begin", newElement.innerText);
327327
testing.expectEqual("afterbegin", newElement.className);
328328
</script>
329+
330+
<script id=nonBreakingSpace>
331+
// Test non-breaking space encoding (critical for React hydration)
332+
const div = document.createElement('div');
333+
div.innerHTML = 'hello\xa0world';
334+
testing.expectEqual('hello\xa0world', div.textContent);
335+
testing.expectEqual('hello&nbsp;world', div.innerHTML);
336+
337+
// Test that outerHTML also encodes non-breaking spaces correctly
338+
const p = document.createElement('p');
339+
p.textContent = 'XAnge\xa0Privacy';
340+
testing.expectEqual('<p>XAnge&nbsp;Privacy</p>', p.outerHTML);
341+
</script>

0 commit comments

Comments
 (0)