Passes all test for WPT dom/nodes/Node-properties.htm

Documents created via DOMImplementation should not inherit the page.URL.

Add textContent for DocumentFragment

Fix creteElement for XML namespace when not explicitly specified
This commit is contained in:
Karl Seguin
2026-02-11 21:20:16 +08:00
parent 77aa2241dc
commit 23c8616ba5
3 changed files with 10 additions and 9 deletions

View File

@@ -34,6 +34,7 @@ pub fn createDocumentType(_: *const DOMImplementation, qualified_name: []const u
pub fn createHTMLDocument(_: *const DOMImplementation, title: ?[]const u8, page: *Page) !*Document { pub fn createHTMLDocument(_: *const DOMImplementation, title: ?[]const u8, page: *Page) !*Document {
const document = (try page._factory.document(Node.Document.HTMLDocument{ ._proto = undefined })).asDocument(); const document = (try page._factory.document(Node.Document.HTMLDocument{ ._proto = undefined })).asDocument();
document._ready_state = .complete; document._ready_state = .complete;
document._url = "about:blank";
{ {
const doctype = try page._factory.node(DocumentType{ const doctype = try page._factory.node(DocumentType{
@@ -67,6 +68,7 @@ pub fn createHTMLDocument(_: *const DOMImplementation, title: ?[]const u8, page:
pub fn createDocument(_: *const DOMImplementation, namespace_: ?[]const u8, qualified_name: ?[]const u8, doctype: ?*DocumentType, page: *Page) !*Document { pub fn createDocument(_: *const DOMImplementation, namespace_: ?[]const u8, qualified_name: ?[]const u8, doctype: ?*DocumentType, page: *Page) !*Document {
// Create XML Document // Create XML Document
const document = (try page._factory.document(Node.Document.XMLDocument{ ._proto = undefined })).asDocument(); const document = (try page._factory.document(Node.Document.XMLDocument{ ._proto = undefined })).asDocument();
document._url = "about:blank";
// Append doctype if provided // Append doctype if provided
if (doctype) |dt| { if (doctype) |dt| {

View File

@@ -44,6 +44,7 @@ const Document = @This();
_type: Type, _type: Type,
_proto: *Node, _proto: *Node,
_location: ?*Location = null, _location: ?*Location = null,
_url: ?[:0]const u8 = null, // URL for documents created via DOMImplementation (about:blank)
_ready_state: ReadyState = .loading, _ready_state: ReadyState = .loading,
_current_script: ?*Element.Html.Script = null, _current_script: ?*Element.Html.Script = null,
_elements_by_id: std.StringHashMapUnmanaged(*Element) = .empty, _elements_by_id: std.StringHashMapUnmanaged(*Element) = .empty,
@@ -105,8 +106,8 @@ pub fn asEventTarget(self: *Document) *@import("EventTarget.zig") {
return self._proto.asEventTarget(); return self._proto.asEventTarget();
} }
pub fn getURL(_: *const Document, page: *const Page) [:0]const u8 { pub fn getURL(self: *const Document, page: *const Page) [:0]const u8 {
return page.url; return self._url orelse page.url;
} }
pub fn getContentType(self: *const Document) []const u8 { pub fn getContentType(self: *const Document) []const u8 {
@@ -131,8 +132,8 @@ pub fn createElement(self: *Document, name: []const u8, options_: ?CreateElement
if (self._type == .html) { if (self._type == .html) {
break :blk .{ .html, std.ascii.lowerString(&page.buf, name) }; break :blk .{ .html, std.ascii.lowerString(&page.buf, name) };
} }
// Generic and XML documents create XML elements // Generic and XML documents create elements with null namespace
break :blk .{ .xml, name }; break :blk .{ .null, name };
}; };
// HTML documents are case-insensitive - lowercase the tag name // HTML documents are case-insensitive - lowercase the tag name

View File

@@ -255,7 +255,7 @@ pub fn childNodes(self: *const Node, page: *Page) !*collections.ChildNodes {
pub fn getTextContent(self: *Node, writer: *std.Io.Writer) error{WriteFailed}!void { pub fn getTextContent(self: *Node, writer: *std.Io.Writer) error{WriteFailed}!void {
switch (self._type) { switch (self._type) {
.element => { .element, .document_fragment => {
var it = self.childrenIterator(); var it = self.childrenIterator();
while (it.next()) |child| { while (it.next()) |child| {
// ignore comments and processing instructions. // ignore comments and processing instructions.
@@ -268,7 +268,6 @@ pub fn getTextContent(self: *Node, writer: *std.Io.Writer) error{WriteFailed}!vo
.cdata => |c| try writer.writeAll(c.getData()), .cdata => |c| try writer.writeAll(c.getData()),
.document => {}, .document => {},
.document_type => {}, .document_type => {},
.document_fragment => {},
.attribute => |attr| try writer.writeAll(attr._value.str()), .attribute => |attr| try writer.writeAll(attr._value.str()),
} }
} }
@@ -912,16 +911,15 @@ pub const JsApi = struct {
fn _textContext(self: *Node, page: *const Page) !?[]const u8 { fn _textContext(self: *Node, page: *const Page) !?[]const u8 {
// cdata and attributes can return value directly, avoiding the copy // cdata and attributes can return value directly, avoiding the copy
switch (self._type) { switch (self._type) {
.element => |el| { .element, .document_fragment => {
var buf = std.Io.Writer.Allocating.init(page.call_arena); var buf = std.Io.Writer.Allocating.init(page.call_arena);
try el.asNode().getTextContent(&buf.writer); try self.getTextContent(&buf.writer);
return buf.written(); return buf.written();
}, },
.cdata => |cdata| return cdata.getData(), .cdata => |cdata| return cdata.getData(),
.attribute => |attr| return attr._value.str(), .attribute => |attr| return attr._value.str(),
.document => return null, .document => return null,
.document_type => return null, .document_type => return null,
.document_fragment => return null,
} }
} }