Merge pull request #2188 from luckyyyyy/feat/document-writeln

feat(document): implement document.writeln
This commit is contained in:
Pierre Tachoire
2026-04-18 14:19:42 -04:00
committed by GitHub
2 changed files with 49 additions and 0 deletions

View File

@@ -115,8 +115,42 @@
testing.expectEqual('function', typeof document.open);
testing.expectEqual('function', typeof document.close);
testing.expectEqual('function', typeof document.write);
testing.expectEqual('function', typeof document.writeln);
</script>
<!-- Phase 4 Tests: document.writeln -->
<script id=basic_writeln>
document.writeln('<p id="writeln_basic">Line</p>');
testing.expectEqual(true, true);
</script>
<script id=verify_writeln_basic>
const el = document.getElementById('writeln_basic');
testing.expectEqual('Line', el.textContent);
// writeln should append a newline after the written text; that newline
// becomes a text node sibling of the element.
const next = el.nextSibling;
testing.expectEqual(true, next !== null);
testing.expectEqual('#text', next.nodeName);
testing.expectEqual('\n', next.nodeValue);
</script>
<script id=writeln_multiple_args>
// writeln joins all arguments, then appends a single U+000A.
document.writeln('<span id="wln_a">A</span>', '<span id="wln_b">B</span>');
testing.expectEqual(true, true);
</script>
<script id=verify_writeln_multiple_args>
const a = document.getElementById('wln_a');
const b = document.getElementById('wln_b');
testing.expectEqual('A', a.textContent);
testing.expectEqual('B', b.textContent);
testing.expectEqual(b, a.nextElementSibling);
</script>
<!-- Phase 3 Tests: document.open/close (post-parsing with setTimeout) -->
<div id="will_be_removed">This will be removed by document.open()</div>

View File

@@ -621,6 +621,17 @@ fn looksLikeNewDocument(html: []const u8) bool {
}
pub fn write(self: *Document, text: []const []const u8, page: *Page) !void {
return self.writeInternal(text, false, page);
}
// https://html.spec.whatwg.org/multipage/dynamic-markup-insertion.html#dom-document-writeln
// `writeln(...text)` runs the document write steps with `text` followed by a
// U+000A LINE FEED character.
pub fn writeln(self: *Document, text: []const []const u8, page: *Page) !void {
return self.writeInternal(text, true, page);
}
fn writeInternal(self: *Document, text: []const []const u8, append_newline: bool, page: *Page) !void {
if (self._type == .xml) {
return error.InvalidStateError;
}
@@ -634,6 +645,9 @@ pub fn write(self: *Document, text: []const []const u8, page: *Page) !void {
for (text) |str| {
try joined.appendSlice(page.call_arena, str);
}
if (append_newline) {
try joined.append(page.call_arena, '\n');
}
break :blk joined.items;
};
@@ -1052,6 +1066,7 @@ pub const JsApi = struct {
pub const elementFromPoint = bridge.function(Document.elementFromPoint, .{});
pub const elementsFromPoint = bridge.function(Document.elementsFromPoint, .{});
pub const write = bridge.function(Document.write, .{ .dom_exception = true });
pub const writeln = bridge.function(Document.writeln, .{ .dom_exception = true });
pub const open = bridge.function(Document.open, .{ .dom_exception = true });
pub const close = bridge.function(Document.close, .{ .dom_exception = true });
pub const doctype = bridge.accessor(Document.getDocType, null, .{});