From 6566df6338a7023324cfbd86c9ce7e1e46a8d0d0 Mon Sep 17 00:00:00 2001 From: Pierre Tachoire Date: Tue, 12 Dec 2023 15:22:33 +0100 Subject: [PATCH 01/12] dom: refacto html collection to handle root or descendant only HTMLCollection works on two different way: * by default it must walk over root's descendants only * on document, since the given root must be included in the walk. --- src/dom/document.zig | 2 + src/dom/html_collection.zig | 149 ++++++++++++++++++++++-------------- 2 files changed, 93 insertions(+), 58 deletions(-) diff --git a/src/dom/document.zig b/src/dom/document.zig index 8bb4577d..713313fa 100644 --- a/src/dom/document.zig +++ b/src/dom/document.zig @@ -110,6 +110,7 @@ pub const Document = struct { alloc, parser.elementToNode(root), tag_name, + true, ); } @@ -123,6 +124,7 @@ pub const Document = struct { alloc, parser.elementToNode(root), classNames, + true, ); } diff --git a/src/dom/html_collection.zig b/src/dom/html_collection.zig index ec32d2a2..63b7eb7b 100644 --- a/src/dom/html_collection.zig +++ b/src/dom/html_collection.zig @@ -14,15 +14,18 @@ const Union = @import("element.zig").Union; const Matcher = union(enum) { matchByTagName: MatchByTagName, matchByClassName: MatchByClassName, + matchTrue: struct {}, pub fn match(self: Matcher, node: *parser.Node) !bool { switch (self) { + inline .matchTrue => return true, inline else => |case| return case.match(node), } } pub fn deinit(self: Matcher, alloc: std.mem.Allocator) void { switch (self) { + .matchTrue => return, inline else => |case| return case.deinit(alloc), } } @@ -56,12 +59,15 @@ pub fn HTMLCollectionByTagName( alloc: std.mem.Allocator, root: *parser.Node, tag_name: []const u8, + include_root: bool, ) !HTMLCollection { return HTMLCollection{ .root = root, + .walker = Walker{ .walkerDepthFirst = .{} }, .matcher = Matcher{ .matchByTagName = try MatchByTagName.init(alloc, tag_name), }, + .include_root = include_root, }; } @@ -97,15 +103,78 @@ pub fn HTMLCollectionByClassName( alloc: std.mem.Allocator, root: *parser.Node, classNames: []const u8, + include_root: bool, ) !HTMLCollection { return HTMLCollection{ .root = root, + .walker = Walker{ .walkerDepthFirst = .{} }, .matcher = Matcher{ .matchByClassName = try MatchByClassName.init(alloc, classNames), }, + .include_root = include_root, }; } +const Walker = union(enum) { + walkerDepthFirst: WalkerDepthFirst, + + pub fn get_next(self: Walker, root: *parser.Node, cur: ?*parser.Node) !?*parser.Node { + switch (self) { + inline else => |case| return case.get_next(root, cur), + } + } +}; + +// WalkerDepthFirst iterates over the DOM tree to return the next following +// node or null at the end. +// +// This implementation is a zig version of Netsurf code. +// http://source.netsurf-browser.org/libdom.git/tree/src/html/html_collection.c#n177 +// +// The iteration is a depth first as required by the specification. +// https://dom.spec.whatwg.org/#htmlcollection +// https://dom.spec.whatwg.org/#concept-tree-order +pub const WalkerDepthFirst = struct { + pub fn get_next(_: WalkerDepthFirst, root: *parser.Node, cur: ?*parser.Node) !?*parser.Node { + var n = cur orelse root; + + // TODO deinit next + if (try parser.nodeFirstChild(n)) |next| { + return next; + } + + // TODO deinit next + if (try parser.nodeNextSibling(n)) |next| { + return next; + } + + // TODO deinit parent + // Back to the parent of cur. + // If cur has no parent, then the iteration is over. + var parent = try parser.nodeParentNode(n) orelse return null; + + // TODO deinit lastchild + var lastchild = try parser.nodeLastChild(parent); + while (n != root and n == lastchild) { + n = parent; + + // TODO deinit parent + // Back to the prev's parent. + // If prev has no parent, then the loop must stop. + parent = try parser.nodeParentNode(n) orelse break; + + // TODO deinit lastchild + lastchild = try parser.nodeLastChild(parent); + } + + if (n == root) { + return null; + } + + return try parser.nodeNextSibling(n); + } +}; + // WEB IDL https://dom.spec.whatwg.org/#htmlcollection // HTMLCollection is re implemented in zig here because libdom // dom_html_collection expects a comparison function callback as arguement. @@ -114,58 +183,26 @@ pub const HTMLCollection = struct { pub const mem_guarantied = true; matcher: Matcher, + walker: Walker, root: *parser.Node, + // By default the HTMLCollection walk on the root's descendant only. + // But on somes cases, like for dom document, we want to walk over the root + // itself. + include_root: bool = false, + // save a state for the collection to improve the _item speed. cur_idx: ?u32 = undefined, cur_node: ?*parser.Node = undefined, - // get_next iterates over the DOM tree to return the next following node or - // null at the end. - // - // This implementation is a zig version of Netsurf code. - // http://source.netsurf-browser.org/libdom.git/tree/src/html/html_collection.c#n177 - // - // The iteration is a depth first as required by the specification. - // https://dom.spec.whatwg.org/#htmlcollection - // https://dom.spec.whatwg.org/#concept-tree-order - fn get_next(root: *parser.Node, cur: *parser.Node) !?*parser.Node { - // TODO deinit next - if (try parser.nodeFirstChild(cur)) |next| { - return next; + // start returns the first node to walk on. + fn start(self: HTMLCollection) !?*parser.Node { + if (self.include_root) { + return self.root; } - // TODO deinit next - if (try parser.nodeNextSibling(cur)) |next| { - return next; - } - - // TODO deinit parent - // Back to the parent of cur. - // If cur has no parent, then the iteration is over. - var parent = try parser.nodeParentNode(cur) orelse return null; - - // TODO deinit lastchild - var lastchild = try parser.nodeLastChild(parent); - var prev = cur; - while (prev != root and prev == lastchild) { - prev = parent; - - // TODO deinit parent - // Back to the prev's parent. - // If prev has no parent, then the loop must stop. - parent = try parser.nodeParentNode(prev) orelse break; - - // TODO deinit lastchild - lastchild = try parser.nodeLastChild(parent); - } - - if (prev == root) { - return null; - } - - return try parser.nodeNextSibling(prev); + return try self.walker.get_next(self.root, null); } /// get_length computes the collection's length dynamically according to @@ -173,18 +210,16 @@ pub const HTMLCollection = struct { // TODO: nodes retrieved must be de-referenced. pub fn get_length(self: *HTMLCollection) !u32 { var len: u32 = 0; - var node: *parser.Node = self.root; - var ntype: parser.NodeType = undefined; + var node = try self.start() orelse return 0; while (true) { - ntype = try parser.nodeType(node); - if (ntype == .element) { + if (try parser.nodeType(node) == .element) { if (try self.matcher.match(node)) { len += 1; } } - node = try get_next(self.root, node) orelse break; + node = try self.walker.get_next(self.root, node) orelse break; } return len; @@ -192,18 +227,18 @@ pub const HTMLCollection = struct { pub fn _item(self: *HTMLCollection, index: u32) !?Union { var i: u32 = 0; - var node: *parser.Node = self.root; - var ntype: parser.NodeType = undefined; + var node: *parser.Node = undefined; // Use the current state to improve speed if possible. if (self.cur_idx != null and index >= self.cur_idx.?) { i = self.cur_idx.?; node = self.cur_node.?; + } else { + node = try self.start() orelse return null; } while (true) { - ntype = try parser.nodeType(node); - if (ntype == .element) { + if (try parser.nodeType(node) == .element) { if (try self.matcher.match(node)) { // check if we found the searched element. if (i == index) { @@ -219,7 +254,7 @@ pub const HTMLCollection = struct { } } - node = try get_next(self.root, node) orelse break; + node = try self.walker.get_next(self.root, node) orelse break; } return null; @@ -230,12 +265,10 @@ pub const HTMLCollection = struct { return null; } - var node: *parser.Node = self.root; - var ntype: parser.NodeType = undefined; + var node = try self.start() orelse return null; while (true) { - ntype = try parser.nodeType(node); - if (ntype == .element) { + if (try parser.nodeType(node) == .element) { if (try self.matcher.match(node)) { const elem = @as(*parser.Element, @ptrCast(node)); @@ -253,7 +286,7 @@ pub const HTMLCollection = struct { } } - node = try get_next(self.root, node) orelse break; + node = try self.walker.get_next(self.root, node) orelse break; } return null; From 6f915373545d14d5c9c77a65a361deba875aa156 Mon Sep 17 00:00:00 2001 From: Pierre Tachoire Date: Tue, 12 Dec 2023 15:24:01 +0100 Subject: [PATCH 02/12] dom: add document.children --- src/dom/document.zig | 7 +++++++ src/dom/html_collection.zig | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/src/dom/document.zig b/src/dom/document.zig index 713313fa..72537992 100644 --- a/src/dom/document.zig +++ b/src/dom/document.zig @@ -166,6 +166,13 @@ pub const Document = struct { return try parser.documentCreateAttributeNS(self, ns, qname); } + // ParentNode + // https://dom.spec.whatwg.org/#parentnode + pub fn get_children(self: *parser.Document) !collection.HTMLCollection { + const root = try parser.documentGetDocumentElement(self); + return try collection.HTMLCollectionChildren(parser.elementToNode(root), true); + } + pub fn deinit(_: *parser.Document, _: std.mem.Allocator) void {} }; diff --git a/src/dom/html_collection.zig b/src/dom/html_collection.zig index 63b7eb7b..871b1d8d 100644 --- a/src/dom/html_collection.zig +++ b/src/dom/html_collection.zig @@ -115,8 +115,21 @@ pub fn HTMLCollectionByClassName( }; } +pub fn HTMLCollectionChildren( + root: *parser.Node, + include_root: bool, +) !HTMLCollection { + return HTMLCollection{ + .root = root, + .walker = Walker{ .walkerChildren = .{} }, + .matcher = Matcher{ .matchTrue = .{} }, + .include_root = include_root, + }; +} + const Walker = union(enum) { walkerDepthFirst: WalkerDepthFirst, + walkerChildren: WalkerChildren, pub fn get_next(self: Walker, root: *parser.Node, cur: ?*parser.Node) !?*parser.Node { switch (self) { @@ -175,6 +188,27 @@ pub const WalkerDepthFirst = struct { } }; +// WalkerChildren iterates over the root's children only. +pub const WalkerChildren = struct { + pub fn get_next(_: WalkerChildren, root: *parser.Node, cur: ?*parser.Node) !?*parser.Node { + // On wlak start, we return the first root's child. + if (cur == null) return try parser.nodeFirstChild(root); + + // If cur is root, then return null. + // This is a special case, if the root is included in the walk, we + // don't want to go further to find children. + if (root == cur.?) return null; + + // TODO deinit last. + const last = try parser.nodeLastChild(root); + if (last == cur.?) { + return null; + } + + return try parser.nodeNextSibling(cur.?); + } +}; + // WEB IDL https://dom.spec.whatwg.org/#htmlcollection // HTMLCollection is re implemented in zig here because libdom // dom_html_collection expects a comparison function callback as arguement. @@ -323,6 +357,8 @@ pub fn testExecFn( .{ .src = "getElementsByTagNameAll.item(7).localName", .ex = "p" }, .{ .src = "getElementsByTagNameAll.namedItem('para-empty-child').localName", .ex = "span" }, + .{ .src = "document.children.length", .ex = "1" }, + // check liveness .{ .src = "let content = document.getElementById('content')", .ex = "undefined" }, .{ .src = "let pe = document.getElementById('para-empty')", .ex = "undefined" }, From fb2ba6700ebb0a73e3d3523cf4c741de0af2faee Mon Sep 17 00:00:00 2001 From: Pierre Tachoire Date: Tue, 12 Dec 2023 15:24:44 +0100 Subject: [PATCH 03/12] dom: add element children and getelementsby* --- src/dom/element.zig | 36 ++++++++++++++++++++++++++++++++++++ src/dom/html_collection.zig | 5 +++++ 2 files changed, 41 insertions(+) diff --git a/src/dom/element.zig b/src/dom/element.zig index aa8bc2f5..c696c61f 100644 --- a/src/dom/element.zig +++ b/src/dom/element.zig @@ -6,6 +6,8 @@ const jsruntime = @import("jsruntime"); const Case = jsruntime.test_utils.Case; const checkCases = jsruntime.test_utils.checkCases; +const collection = @import("html_collection.zig"); + const Node = @import("node.zig").Node; const HTMLElem = @import("../html/elements.zig"); pub const Union = @import("../html/elements.zig").Union; @@ -122,6 +124,40 @@ pub const Element = struct { // Return true. return true; } + + pub fn _getElementsByTagName( + self: *parser.Element, + alloc: std.mem.Allocator, + tag_name: []const u8, + ) !collection.HTMLCollection { + return try collection.HTMLCollectionByTagName( + alloc, + parser.elementToNode(self), + tag_name, + false, + ); + } + + pub fn _getElementsByClassName( + self: *parser.Element, + alloc: std.mem.Allocator, + classNames: []const u8, + ) !collection.HTMLCollection { + return try collection.HTMLCollectionByClassName( + alloc, + parser.elementToNode(self), + classNames, + false, + ); + } + + // ParentNode + // https://dom.spec.whatwg.org/#parentnode + pub fn get_children(self: *parser.Element) !collection.HTMLCollection { + return try collection.HTMLCollectionChildren(parser.elementToNode(self), false); + } + + pub fn deinit(_: *parser.Element, _: std.mem.Allocator) void {} }; // Tests diff --git a/src/dom/html_collection.zig b/src/dom/html_collection.zig index 871b1d8d..5928a35e 100644 --- a/src/dom/html_collection.zig +++ b/src/dom/html_collection.zig @@ -357,7 +357,12 @@ pub fn testExecFn( .{ .src = "getElementsByTagNameAll.item(7).localName", .ex = "p" }, .{ .src = "getElementsByTagNameAll.namedItem('para-empty-child').localName", .ex = "span" }, + .{ .src = "document.getElementById('content').getElementsByTagName('*').length", .ex = "4" }, + .{ .src = "document.getElementById('content').getElementsByTagName('p').length", .ex = "2" }, + .{ .src = "document.getElementById('content').getElementsByTagName('div').length", .ex = "0" }, + .{ .src = "document.children.length", .ex = "1" }, + .{ .src = "document.getElementById('content').children.length", .ex = "3" }, // check liveness .{ .src = "let content = document.getElementById('content')", .ex = "undefined" }, From edff9f7444c5342dfe6dd37dbe6183d76a2ff198 Mon Sep 17 00:00:00 2001 From: Pierre Tachoire Date: Tue, 12 Dec 2023 15:27:37 +0100 Subject: [PATCH 04/12] dom: element: add wpt test --- tests/wpt/dom/nodes/Element-children.html | 58 +++++++++++++++++++ .../nodes/Element-getElementsByClassName.html | 43 ++++++++++++++ ...agName-change-document-HTMLNess-iframe.xml | 1 + ...ntsByTagName-change-document-HTMLNess.html | 51 ++++++++++++++++ .../nodes/Element-getElementsByTagName.html | 30 ++++++++++ .../nodes/Element-getElementsByTagNameNS.html | 37 ++++++++++++ 6 files changed, 220 insertions(+) create mode 100644 tests/wpt/dom/nodes/Element-children.html create mode 100644 tests/wpt/dom/nodes/Element-getElementsByClassName.html create mode 100644 tests/wpt/dom/nodes/Element-getElementsByTagName-change-document-HTMLNess-iframe.xml create mode 100644 tests/wpt/dom/nodes/Element-getElementsByTagName-change-document-HTMLNess.html create mode 100644 tests/wpt/dom/nodes/Element-getElementsByTagName.html create mode 100644 tests/wpt/dom/nodes/Element-getElementsByTagNameNS.html diff --git a/tests/wpt/dom/nodes/Element-children.html b/tests/wpt/dom/nodes/Element-children.html new file mode 100644 index 00000000..c0210f96 --- /dev/null +++ b/tests/wpt/dom/nodes/Element-children.html @@ -0,0 +1,58 @@ + +HTMLCollection edge cases + + +
+
+ diff --git a/tests/wpt/dom/nodes/Element-getElementsByClassName.html b/tests/wpt/dom/nodes/Element-getElementsByClassName.html new file mode 100644 index 00000000..bc87b05d --- /dev/null +++ b/tests/wpt/dom/nodes/Element-getElementsByClassName.html @@ -0,0 +1,43 @@ + +Element.getElementsByClassName + + +
+ diff --git a/tests/wpt/dom/nodes/Element-getElementsByTagName-change-document-HTMLNess-iframe.xml b/tests/wpt/dom/nodes/Element-getElementsByTagName-change-document-HTMLNess-iframe.xml new file mode 100644 index 00000000..f3f286ea --- /dev/null +++ b/tests/wpt/dom/nodes/Element-getElementsByTagName-change-document-HTMLNess-iframe.xml @@ -0,0 +1 @@ + diff --git a/tests/wpt/dom/nodes/Element-getElementsByTagName-change-document-HTMLNess.html b/tests/wpt/dom/nodes/Element-getElementsByTagName-change-document-HTMLNess.html new file mode 100644 index 00000000..c41ee2e8 --- /dev/null +++ b/tests/wpt/dom/nodes/Element-getElementsByTagName-change-document-HTMLNess.html @@ -0,0 +1,51 @@ + + + + + + + diff --git a/tests/wpt/dom/nodes/Element-getElementsByTagName.html b/tests/wpt/dom/nodes/Element-getElementsByTagName.html new file mode 100644 index 00000000..87c4fe93 --- /dev/null +++ b/tests/wpt/dom/nodes/Element-getElementsByTagName.html @@ -0,0 +1,30 @@ + + +Element.getElementsByTagName + + + + +
+ diff --git a/tests/wpt/dom/nodes/Element-getElementsByTagNameNS.html b/tests/wpt/dom/nodes/Element-getElementsByTagNameNS.html new file mode 100644 index 00000000..f826afc3 --- /dev/null +++ b/tests/wpt/dom/nodes/Element-getElementsByTagNameNS.html @@ -0,0 +1,37 @@ + + +Element.getElementsByTagNameNS + + + + +
+ From 82c836c80d14a92c2e4e7ddf5ab23f6bdc0fcbd0 Mon Sep 17 00:00:00 2001 From: Pierre Tachoire Date: Wed, 13 Dec 2023 13:12:01 +0100 Subject: [PATCH 05/12] dom: document first element can be null --- src/dom/document.zig | 44 ++++++++++++++++++++++--------------- src/dom/html_collection.zig | 29 ++++++++++++++---------- src/netsurf.zig | 3 ++- 3 files changed, 45 insertions(+), 31 deletions(-) diff --git a/src/dom/document.zig b/src/dom/document.zig index 72537992..791b1440 100644 --- a/src/dom/document.zig +++ b/src/dom/document.zig @@ -34,9 +34,10 @@ pub const Document = struct { return DOMImplementation{}; } - pub fn get_documentElement(self: *parser.Document) !ElementUnion { + pub fn get_documentElement(self: *parser.Document) !?ElementUnion { const e = try parser.documentGetDocumentElement(self); - return try Element.toInterface(e); + if (e == null) return null; + return try Element.toInterface(e.?); } pub fn get_documentURI(self: *parser.Document) ![]const u8 { @@ -105,13 +106,12 @@ pub const Document = struct { alloc: std.mem.Allocator, tag_name: []const u8, ) !collection.HTMLCollection { - const root = try parser.documentGetDocumentElement(self); - return try collection.HTMLCollectionByTagName( - alloc, - parser.elementToNode(root), - tag_name, - true, - ); + var elt: ?*parser.Node = null; + if (try parser.documentGetDocumentElement(self)) |root| { + elt = parser.elementToNode(root); + } + + return try collection.HTMLCollectionByTagName(alloc, elt, tag_name, true); } pub fn _getElementsByClassName( @@ -119,13 +119,12 @@ pub const Document = struct { alloc: std.mem.Allocator, classNames: []const u8, ) !collection.HTMLCollection { - const root = try parser.documentGetDocumentElement(self); - return try collection.HTMLCollectionByClassName( - alloc, - parser.elementToNode(root), - classNames, - true, - ); + var elt: ?*parser.Node = null; + if (try parser.documentGetDocumentElement(self)) |root| { + elt = parser.elementToNode(root); + } + + return try collection.HTMLCollectionByClassName(alloc, elt, classNames, true); } pub fn _createDocumentFragment(self: *parser.Document) !*parser.DocumentFragment { @@ -169,8 +168,11 @@ pub const Document = struct { // ParentNode // https://dom.spec.whatwg.org/#parentnode pub fn get_children(self: *parser.Document) !collection.HTMLCollection { - const root = try parser.documentGetDocumentElement(self); - return try collection.HTMLCollectionChildren(parser.elementToNode(root), true); + var elt: ?*parser.Node = null; + if (try parser.documentGetDocumentElement(self)) |root| { + elt = parser.elementToNode(root); + } + return try collection.HTMLCollectionChildren(elt, true); } pub fn deinit(_: *parser.Document, _: std.mem.Allocator) void {} @@ -188,6 +190,12 @@ pub fn testExecFn( .{ .src = "document.__proto__.__proto__.constructor.name", .ex = "Document" }, .{ .src = "document.__proto__.__proto__.__proto__.constructor.name", .ex = "Node" }, .{ .src = "document.__proto__.__proto__.__proto__.__proto__.constructor.name", .ex = "EventTarget" }, + + .{ .src = "let newdoc = new Document()", .ex = "undefined" }, + .{ .src = "newdoc.documentElement", .ex = "null" }, + .{ .src = "newdoc.children.length", .ex = "0" }, + .{ .src = "newdoc.getElementsByTagName('*').length", .ex = "0" }, + .{ .src = "newdoc.getElementsByTagName('*').item(0)", .ex = "null" }, }; try checkCases(js_env, &constructor); diff --git a/src/dom/html_collection.zig b/src/dom/html_collection.zig index 5928a35e..f3191ceb 100644 --- a/src/dom/html_collection.zig +++ b/src/dom/html_collection.zig @@ -57,7 +57,7 @@ pub const MatchByTagName = struct { pub fn HTMLCollectionByTagName( alloc: std.mem.Allocator, - root: *parser.Node, + root: ?*parser.Node, tag_name: []const u8, include_root: bool, ) !HTMLCollection { @@ -101,7 +101,7 @@ pub const MatchByClassName = struct { pub fn HTMLCollectionByClassName( alloc: std.mem.Allocator, - root: *parser.Node, + root: ?*parser.Node, classNames: []const u8, include_root: bool, ) !HTMLCollection { @@ -116,7 +116,7 @@ pub fn HTMLCollectionByClassName( } pub fn HTMLCollectionChildren( - root: *parser.Node, + root: ?*parser.Node, include_root: bool, ) !HTMLCollection { return HTMLCollection{ @@ -219,7 +219,7 @@ pub const HTMLCollection = struct { matcher: Matcher, walker: Walker, - root: *parser.Node, + root: ?*parser.Node, // By default the HTMLCollection walk on the root's descendant only. // But on somes cases, like for dom document, we want to walk over the root @@ -232,17 +232,21 @@ pub const HTMLCollection = struct { // start returns the first node to walk on. fn start(self: HTMLCollection) !?*parser.Node { + if (self.root == null) return null; + if (self.include_root) { - return self.root; + return self.root.?; } - return try self.walker.get_next(self.root, null); + return try self.walker.get_next(self.root.?, null); } /// get_length computes the collection's length dynamically according to /// the current root structure. // TODO: nodes retrieved must be de-referenced. pub fn get_length(self: *HTMLCollection) !u32 { + if (self.root == null) return 0; + var len: u32 = 0; var node = try self.start() orelse return 0; @@ -253,13 +257,15 @@ pub const HTMLCollection = struct { } } - node = try self.walker.get_next(self.root, node) orelse break; + node = try self.walker.get_next(self.root.?, node) orelse break; } return len; } pub fn _item(self: *HTMLCollection, index: u32) !?Union { + if (self.root == null) return null; + var i: u32 = 0; var node: *parser.Node = undefined; @@ -288,16 +294,15 @@ pub const HTMLCollection = struct { } } - node = try self.walker.get_next(self.root, node) orelse break; + node = try self.walker.get_next(self.root.?, node) orelse break; } return null; } pub fn _namedItem(self: *HTMLCollection, name: []const u8) !?Union { - if (name.len == 0) { - return null; - } + if (self.root == null) return null; + if (name.len == 0) return null; var node = try self.start() orelse return null; @@ -320,7 +325,7 @@ pub const HTMLCollection = struct { } } - node = try self.walker.get_next(self.root, node) orelse break; + node = try self.walker.get_next(self.root.?, node) orelse break; } return null; diff --git a/src/netsurf.zig b/src/netsurf.zig index 3f8336ff..2455b35a 100644 --- a/src/netsurf.zig +++ b/src/netsurf.zig @@ -1150,10 +1150,11 @@ pub inline fn documentGetElementsByTagName(doc: *Document, tagname: []const u8) } // documentGetDocumentElement returns the root document element. -pub inline fn documentGetDocumentElement(doc: *Document) !*Element { +pub inline fn documentGetDocumentElement(doc: *Document) !?*Element { var elem: ?*Element = undefined; const err = documentVtable(doc).dom_document_get_document_element.?(doc, &elem); try DOMErr(err); + if (elem == null) return null; return elem.?; } From c732b7496cc640f2408c4e19ef5a0fb1358a1757 Mon Sep 17 00:00:00 2001 From: Pierre Tachoire Date: Wed, 13 Dec 2023 13:17:45 +0100 Subject: [PATCH 06/12] dom: implement document parentNode accessors --- src/dom/document.zig | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/dom/document.zig b/src/dom/document.zig index 791b1440..846c2f32 100644 --- a/src/dom/document.zig +++ b/src/dom/document.zig @@ -175,6 +175,21 @@ pub const Document = struct { return try collection.HTMLCollectionChildren(elt, true); } + pub fn get_firstElementChild(self: *parser.Document) !?ElementUnion { + const elt = try parser.documentGetDocumentElement(self) orelse return null; + return try Element.toInterface(elt); + } + + pub fn get_lastElementChild(self: *parser.Document) !?ElementUnion { + const elt = try parser.documentGetDocumentElement(self) orelse return null; + return try Element.toInterface(elt); + } + + pub fn get_childElementCount(self: *parser.Document) !u32 { + _ = try parser.documentGetDocumentElement(self) orelse return 0; + return 1; + } + pub fn deinit(_: *parser.Document, _: std.mem.Allocator) void {} }; @@ -323,6 +338,22 @@ pub fn testExecFn( }; try checkCases(js_env, &createAttr); + var parentNode = [_]Case{ + .{ .src = "document.children.length", .ex = "1" }, + .{ .src = "document.children.item(0).nodeName", .ex = "HTML" }, + .{ .src = "document.firstElementChild.nodeName", .ex = "HTML" }, + .{ .src = "document.lastElementChild.nodeName", .ex = "HTML" }, + .{ .src = "document.childElementCount", .ex = "1" }, + + .{ .src = "let nd = new Document()", .ex = "undefined" }, + .{ .src = "nd.children.length", .ex = "0" }, + .{ .src = "nd.children.item(0)", .ex = "null" }, + .{ .src = "nd.firstElementChild", .ex = "null" }, + .{ .src = "nd.lastElementChild", .ex = "null" }, + .{ .src = "nd.childElementCount", .ex = "0" }, + }; + try checkCases(js_env, &parentNode); + const tags = comptime parser.Tag.all(); comptime var createElements: [(tags.len) * 2]Case = undefined; inline for (tags, 0..) |tag, i| { From d1f8555c1eba1a370b8b3b9ea7e47ba74ae0d0cd Mon Sep 17 00:00:00 2001 From: Pierre Tachoire Date: Wed, 13 Dec 2023 13:38:24 +0100 Subject: [PATCH 07/12] element: implement parenteNode accessors --- src/dom/element.zig | 29 ++++++++++ .../wpt/dom/Element-childElement-null-svg.svg | 20 +++++++ .../dom/Element-childElement-null-xhtml.xhtml | 20 +++++++ tests/wpt/dom/Element-childElement-null.html | 15 +++++ ...ment-childElementCount-dynamic-add-svg.svg | 22 +++++++ ...-childElementCount-dynamic-add-xhtml.xhtml | 22 +++++++ ...Element-childElementCount-dynamic-add.html | 17 ++++++ ...t-childElementCount-dynamic-remove-svg.svg | 22 +++++++ ...ildElementCount-dynamic-remove-xhtml.xhtml | 22 +++++++ ...ment-childElementCount-dynamic-remove.html | 17 ++++++ .../Element-childElementCount-nochild-svg.svg | 19 ++++++ ...ment-childElementCount-nochild-xhtml.xhtml | 19 ++++++ .../Element-childElementCount-nochild.html | 14 +++++ .../wpt/dom/Element-childElementCount-svg.svg | 25 ++++++++ .../dom/Element-childElementCount-xhtml.xhtml | 25 ++++++++ tests/wpt/dom/Element-childElementCount.html | 20 +++++++ tests/wpt/dom/Element-children.html | 58 +++++++++++++++++++ ...ement-firstElementChild-entity-xhtml.xhtml | 27 +++++++++ .../dom/Element-firstElementChild-entity.svg | 26 +++++++++ ...lement-firstElementChild-namespace-svg.svg | 26 +++++++++ ...nt-firstElementChild-namespace-xhtml.xhtml | 28 +++++++++ .../Element-firstElementChild-namespace.html | 21 +++++++ .../wpt/dom/Element-firstElementChild-svg.svg | 23 ++++++++ .../dom/Element-firstElementChild-xhtml.xhtml | 23 ++++++++ tests/wpt/dom/Element-firstElementChild.html | 18 ++++++ .../wpt/dom/Element-lastElementChild-svg.svg | 22 +++++++ .../dom/Element-lastElementChild-xhtml.xhtml | 22 +++++++ tests/wpt/dom/Element-lastElementChild.html | 17 ++++++ 28 files changed, 639 insertions(+) create mode 100644 tests/wpt/dom/Element-childElement-null-svg.svg create mode 100644 tests/wpt/dom/Element-childElement-null-xhtml.xhtml create mode 100644 tests/wpt/dom/Element-childElement-null.html create mode 100644 tests/wpt/dom/Element-childElementCount-dynamic-add-svg.svg create mode 100644 tests/wpt/dom/Element-childElementCount-dynamic-add-xhtml.xhtml create mode 100644 tests/wpt/dom/Element-childElementCount-dynamic-add.html create mode 100644 tests/wpt/dom/Element-childElementCount-dynamic-remove-svg.svg create mode 100644 tests/wpt/dom/Element-childElementCount-dynamic-remove-xhtml.xhtml create mode 100644 tests/wpt/dom/Element-childElementCount-dynamic-remove.html create mode 100644 tests/wpt/dom/Element-childElementCount-nochild-svg.svg create mode 100644 tests/wpt/dom/Element-childElementCount-nochild-xhtml.xhtml create mode 100644 tests/wpt/dom/Element-childElementCount-nochild.html create mode 100644 tests/wpt/dom/Element-childElementCount-svg.svg create mode 100644 tests/wpt/dom/Element-childElementCount-xhtml.xhtml create mode 100644 tests/wpt/dom/Element-childElementCount.html create mode 100644 tests/wpt/dom/Element-children.html create mode 100644 tests/wpt/dom/Element-firstElementChild-entity-xhtml.xhtml create mode 100644 tests/wpt/dom/Element-firstElementChild-entity.svg create mode 100644 tests/wpt/dom/Element-firstElementChild-namespace-svg.svg create mode 100644 tests/wpt/dom/Element-firstElementChild-namespace-xhtml.xhtml create mode 100644 tests/wpt/dom/Element-firstElementChild-namespace.html create mode 100644 tests/wpt/dom/Element-firstElementChild-svg.svg create mode 100644 tests/wpt/dom/Element-firstElementChild-xhtml.xhtml create mode 100644 tests/wpt/dom/Element-firstElementChild.html create mode 100644 tests/wpt/dom/Element-lastElementChild-svg.svg create mode 100644 tests/wpt/dom/Element-lastElementChild-xhtml.xhtml create mode 100644 tests/wpt/dom/Element-lastElementChild.html diff --git a/src/dom/element.zig b/src/dom/element.zig index c696c61f..1a506681 100644 --- a/src/dom/element.zig +++ b/src/dom/element.zig @@ -157,6 +157,26 @@ pub const Element = struct { return try collection.HTMLCollectionChildren(parser.elementToNode(self), false); } + pub fn get_firstElementChild(self: *parser.Element) !?Union { + var children = try get_children(self); + return try children._item(0); + } + + pub fn get_lastElementChild(self: *parser.Element) !?Union { + // TODO we could check the last child node first, if it's an element, + // we can return it directly instead of looping twice over the + // children. + var children = try get_children(self); + const ln = try children.get_length(); + if (ln == 0) return null; + return try children._item(ln - 1); + } + + pub fn get_childElementCount(self: *parser.Element) !u32 { + var children = try get_children(self); + return try children.get_length(); + } + pub fn deinit(_: *parser.Element, _: std.mem.Allocator) void {} }; @@ -228,4 +248,13 @@ pub fn testExecFn( .{ .src = "b.hasAttribute('foo')", .ex = "false" }, }; try checkCases(js_env, &toggleAttr); + + var parentNode = [_]Case{ + .{ .src = "let c = document.getElementById('content')", .ex = "undefined" }, + .{ .src = "c.children.length", .ex = "3" }, + .{ .src = "c.firstElementChild.nodeName", .ex = "A" }, + .{ .src = "c.lastElementChild.nodeName", .ex = "P" }, + .{ .src = "c.childElementCount", .ex = "3" }, + }; + try checkCases(js_env, &parentNode); } diff --git a/tests/wpt/dom/Element-childElement-null-svg.svg b/tests/wpt/dom/Element-childElement-null-svg.svg new file mode 100644 index 00000000..38848287 --- /dev/null +++ b/tests/wpt/dom/Element-childElement-null-svg.svg @@ -0,0 +1,20 @@ + + +Null test + + + +Test of firstElementChild and lastChildElement returning null +Test + + +test(function() { + var parentEl = document.getElementById("parentEl") + assert_equals(parentEl.firstElementChild, null) + assert_equals(parentEl.lastElementChild, null) +}) + + diff --git a/tests/wpt/dom/Element-childElement-null-xhtml.xhtml b/tests/wpt/dom/Element-childElement-null-xhtml.xhtml new file mode 100644 index 00000000..daedab6d --- /dev/null +++ b/tests/wpt/dom/Element-childElement-null-xhtml.xhtml @@ -0,0 +1,20 @@ + + + +Null Test + + + + +

Test of firstElementChild and lastChildElement returning null

+
+

Test.

+ + + diff --git a/tests/wpt/dom/Element-childElement-null.html b/tests/wpt/dom/Element-childElement-null.html new file mode 100644 index 00000000..1863a41d --- /dev/null +++ b/tests/wpt/dom/Element-childElement-null.html @@ -0,0 +1,15 @@ + + +Null test + + +

Test of firstElementChild and lastChildElement returning null

+
+

Test.

+ diff --git a/tests/wpt/dom/Element-childElementCount-dynamic-add-svg.svg b/tests/wpt/dom/Element-childElementCount-dynamic-add-svg.svg new file mode 100644 index 00000000..d149f1ea --- /dev/null +++ b/tests/wpt/dom/Element-childElementCount-dynamic-add-svg.svg @@ -0,0 +1,22 @@ + + +Dynamic Adding of Elements + + + +Test of Dynamic Adding of Elements +The result of this test is +unknown. + + +test(function() { + var parentEl = document.getElementById("parentEl"); + var newChild = document.createElementNS("http://www.w3.org/2000/svg", "tspan"); + parentEl.appendChild(newChild); + assert_equals(parentEl.childElementCount, 2) +}) + + diff --git a/tests/wpt/dom/Element-childElementCount-dynamic-add-xhtml.xhtml b/tests/wpt/dom/Element-childElementCount-dynamic-add-xhtml.xhtml new file mode 100644 index 00000000..c97ed196 --- /dev/null +++ b/tests/wpt/dom/Element-childElementCount-dynamic-add-xhtml.xhtml @@ -0,0 +1,22 @@ + + + +Dynamic Adding of Elements + + + + +

Test of Dynamic Adding of Elements

+
+

The result of this test is +logged above.

+ + + diff --git a/tests/wpt/dom/Element-childElementCount-dynamic-add.html b/tests/wpt/dom/Element-childElementCount-dynamic-add.html new file mode 100644 index 00000000..3e7490b2 --- /dev/null +++ b/tests/wpt/dom/Element-childElementCount-dynamic-add.html @@ -0,0 +1,17 @@ + + +Dynamic Adding of Elements + + +

Test of Dynamic Adding of Elements

+
+

The result of this test is +logged above.

+ diff --git a/tests/wpt/dom/Element-childElementCount-dynamic-remove-svg.svg b/tests/wpt/dom/Element-childElementCount-dynamic-remove-svg.svg new file mode 100644 index 00000000..bf99de65 --- /dev/null +++ b/tests/wpt/dom/Element-childElementCount-dynamic-remove-svg.svg @@ -0,0 +1,22 @@ + + +Dynamic Removal of Elements + + + +Test of Dynamic Removal of Elements +The result of this test is +unknown. + + +test(function() { + var parentEl = document.getElementById("parentEl"); + var lec = parentEl.lastElementChild; + parentEl.removeChild(lec); + assert_equals(parentEl.childElementCount, 1) +}) + + diff --git a/tests/wpt/dom/Element-childElementCount-dynamic-remove-xhtml.xhtml b/tests/wpt/dom/Element-childElementCount-dynamic-remove-xhtml.xhtml new file mode 100644 index 00000000..f0009b0a --- /dev/null +++ b/tests/wpt/dom/Element-childElementCount-dynamic-remove-xhtml.xhtml @@ -0,0 +1,22 @@ + + + +Dynamic Removal of Elements + + + + +

Test of Removal Adding of Elements

+
+

The result of this test is +logged above.

+ + + diff --git a/tests/wpt/dom/Element-childElementCount-dynamic-remove.html b/tests/wpt/dom/Element-childElementCount-dynamic-remove.html new file mode 100644 index 00000000..3f7e7c7e --- /dev/null +++ b/tests/wpt/dom/Element-childElementCount-dynamic-remove.html @@ -0,0 +1,17 @@ + + +Dynamic Removal of Elements + + +

Test of Dynamic Removal of Elements

+
+

The result of this test is +unknown.

+ diff --git a/tests/wpt/dom/Element-childElementCount-nochild-svg.svg b/tests/wpt/dom/Element-childElementCount-nochild-svg.svg new file mode 100644 index 00000000..8ba57436 --- /dev/null +++ b/tests/wpt/dom/Element-childElementCount-nochild-svg.svg @@ -0,0 +1,19 @@ + + +childElementCount + + + +Test of childElementCount with No Child Element Nodes +Test + + +test(function() { + var parentEl = document.getElementById("parentEl") + assert_equals(parentEl.childElementCount, 0) +}) + + diff --git a/tests/wpt/dom/Element-childElementCount-nochild-xhtml.xhtml b/tests/wpt/dom/Element-childElementCount-nochild-xhtml.xhtml new file mode 100644 index 00000000..f567a20c --- /dev/null +++ b/tests/wpt/dom/Element-childElementCount-nochild-xhtml.xhtml @@ -0,0 +1,19 @@ + + + +childElementCount without Child Element Nodes + + + + +

Test of childElementCount with No Child Element Nodes

+
+

Test.

+ + + diff --git a/tests/wpt/dom/Element-childElementCount-nochild.html b/tests/wpt/dom/Element-childElementCount-nochild.html new file mode 100644 index 00000000..fb52fb20 --- /dev/null +++ b/tests/wpt/dom/Element-childElementCount-nochild.html @@ -0,0 +1,14 @@ + + +childElementCount without Child Element Nodes + + +

Test of childElementCount with No Child Element Nodes

+
+

Test.

+ diff --git a/tests/wpt/dom/Element-childElementCount-svg.svg b/tests/wpt/dom/Element-childElementCount-svg.svg new file mode 100644 index 00000000..ff93eff6 --- /dev/null +++ b/tests/wpt/dom/Element-childElementCount-svg.svg @@ -0,0 +1,25 @@ + + +childElementCount + + + +Test of childElementCount +The result of this test is +unknown. + + + + + + +test(function() { + var parentEl = document.getElementById("parentEl") + assert_true("childElementCount" in parentEl) + assert_equals(parentEl.childElementCount, 3) +}) + + diff --git a/tests/wpt/dom/Element-childElementCount-xhtml.xhtml b/tests/wpt/dom/Element-childElementCount-xhtml.xhtml new file mode 100644 index 00000000..6b719ff7 --- /dev/null +++ b/tests/wpt/dom/Element-childElementCount-xhtml.xhtml @@ -0,0 +1,25 @@ + + + +childElementCount + + + + +

Test of childElementCount

+
+

The result of this test is +unknown. + + + +

+ + + diff --git a/tests/wpt/dom/Element-childElementCount.html b/tests/wpt/dom/Element-childElementCount.html new file mode 100644 index 00000000..8cfe567f --- /dev/null +++ b/tests/wpt/dom/Element-childElementCount.html @@ -0,0 +1,20 @@ + + +childElementCount + + +

Test of childElementCount

+
+

The result of this test is +given above. + + + +

+ diff --git a/tests/wpt/dom/Element-children.html b/tests/wpt/dom/Element-children.html new file mode 100644 index 00000000..c0210f96 --- /dev/null +++ b/tests/wpt/dom/Element-children.html @@ -0,0 +1,58 @@ + +HTMLCollection edge cases + + +
+
+ diff --git a/tests/wpt/dom/Element-firstElementChild-entity-xhtml.xhtml b/tests/wpt/dom/Element-firstElementChild-entity-xhtml.xhtml new file mode 100644 index 00000000..f28005e9 --- /dev/null +++ b/tests/wpt/dom/Element-firstElementChild-entity-xhtml.xhtml @@ -0,0 +1,27 @@ + +unknown."> +]> + + +Entity References + + + + +

Test of Entity References

+
+

The result of this test is &tree;

+ + + diff --git a/tests/wpt/dom/Element-firstElementChild-entity.svg b/tests/wpt/dom/Element-firstElementChild-entity.svg new file mode 100644 index 00000000..3a20ea79 --- /dev/null +++ b/tests/wpt/dom/Element-firstElementChild-entity.svg @@ -0,0 +1,26 @@ + +unknown."> +]> + +Entity References + + + +Test of Entity References +The result of this test is &tree; + + +test(function() { + var parentEl = document.getElementById("parentEl") + var fec = parentEl.firstElementChild; + assert_true(!!fec) + assert_equals(fec.nodeType, 1) + assert_equals(fec.getAttribute("id"), "first_element_child") +}) + + diff --git a/tests/wpt/dom/Element-firstElementChild-namespace-svg.svg b/tests/wpt/dom/Element-firstElementChild-namespace-svg.svg new file mode 100644 index 00000000..d42c0877 --- /dev/null +++ b/tests/wpt/dom/Element-firstElementChild-namespace-svg.svg @@ -0,0 +1,26 @@ + + +firstElementChild with namespaces + + + +Test of firstElementChild with namespaces + + + + + +test(function() { + var parentEl = document.getElementById("parentEl"); + var fec = parentEl.firstElementChild; + assert_true(!!fec) + assert_equals(fec.nodeType, 1) + assert_equals(fec.getAttribute("id"), "first_element_child") + assert_equals(fec.localName, "dill") +}) + + diff --git a/tests/wpt/dom/Element-firstElementChild-namespace-xhtml.xhtml b/tests/wpt/dom/Element-firstElementChild-namespace-xhtml.xhtml new file mode 100644 index 00000000..29441d27 --- /dev/null +++ b/tests/wpt/dom/Element-firstElementChild-namespace-xhtml.xhtml @@ -0,0 +1,28 @@ + + + +firstElementChild with namespaces + + + + +

Test of firstElementChild with namespaces

+
+ +
+
+

The result of this test is +logged above.

+ + + diff --git a/tests/wpt/dom/Element-firstElementChild-namespace.html b/tests/wpt/dom/Element-firstElementChild-namespace.html new file mode 100644 index 00000000..629deab3 --- /dev/null +++ b/tests/wpt/dom/Element-firstElementChild-namespace.html @@ -0,0 +1,21 @@ + + +firstElementChild with namespaces + + +

Test of firstElementChild with namespaces

+
+

The result of this test is a unknown.

+ diff --git a/tests/wpt/dom/Element-firstElementChild-svg.svg b/tests/wpt/dom/Element-firstElementChild-svg.svg new file mode 100644 index 00000000..359c5b82 --- /dev/null +++ b/tests/wpt/dom/Element-firstElementChild-svg.svg @@ -0,0 +1,23 @@ + + +firstElementChild + + + +Test of firstElementChild +The result of this test is +unknown. + + +test(function() { + var parentEl = document.getElementById("parentEl"); + var fec = parentEl.firstElementChild; + assert_true(!!fec) + assert_equals(fec.nodeType, 1) + assert_equals(fec.getAttribute("id"), "first_element_child") +}) + + diff --git a/tests/wpt/dom/Element-firstElementChild-xhtml.xhtml b/tests/wpt/dom/Element-firstElementChild-xhtml.xhtml new file mode 100644 index 00000000..302052b0 --- /dev/null +++ b/tests/wpt/dom/Element-firstElementChild-xhtml.xhtml @@ -0,0 +1,23 @@ + + + +firstElementChild + + + + +

Test of firstElementChild

+
+

The result of this test is +logged above.

+ + + diff --git a/tests/wpt/dom/Element-firstElementChild.html b/tests/wpt/dom/Element-firstElementChild.html new file mode 100644 index 00000000..12a0c594 --- /dev/null +++ b/tests/wpt/dom/Element-firstElementChild.html @@ -0,0 +1,18 @@ + + +firstElementChild + + +

Test of firstElementChild

+
+

The result of this test is +logged above.

+ diff --git a/tests/wpt/dom/Element-lastElementChild-svg.svg b/tests/wpt/dom/Element-lastElementChild-svg.svg new file mode 100644 index 00000000..1cec4a13 --- /dev/null +++ b/tests/wpt/dom/Element-lastElementChild-svg.svg @@ -0,0 +1,22 @@ + + +lastElementChild + + + +Test of lastElementChild +The result of this test is not known. + + +test(function() { + var parentEl = document.getElementById("parentEl"); + var lec = parentEl.lastElementChild; + assert_true(!!lec) + assert_equals(lec.nodeType, 1) + assert_equals(lec.getAttribute("id"), "last_element_child") +}) + + diff --git a/tests/wpt/dom/Element-lastElementChild-xhtml.xhtml b/tests/wpt/dom/Element-lastElementChild-xhtml.xhtml new file mode 100644 index 00000000..3150b92a --- /dev/null +++ b/tests/wpt/dom/Element-lastElementChild-xhtml.xhtml @@ -0,0 +1,22 @@ + + + +firstElementChild + + + + +

Test of firstElementChild

+
+

The result of this test is logged above.

+ + + diff --git a/tests/wpt/dom/Element-lastElementChild.html b/tests/wpt/dom/Element-lastElementChild.html new file mode 100644 index 00000000..de7aebdf --- /dev/null +++ b/tests/wpt/dom/Element-lastElementChild.html @@ -0,0 +1,17 @@ + + +lastElementChild + + +

Test of lastElementChild

+
+

The result of this test is logged above.

+ From 61e2cc34daf4889d3774441651deff8d8aecd8af Mon Sep 17 00:00:00 2001 From: Pierre Tachoire Date: Wed, 13 Dec 2023 15:58:11 +0100 Subject: [PATCH 08/12] dom: add ParentNode-children.html wpt test --- tests/wpt/dom/nodes/ParentNode-children.html | 27 ++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 tests/wpt/dom/nodes/ParentNode-children.html diff --git a/tests/wpt/dom/nodes/ParentNode-children.html b/tests/wpt/dom/nodes/ParentNode-children.html new file mode 100644 index 00000000..6621e7d9 --- /dev/null +++ b/tests/wpt/dom/nodes/ParentNode-children.html @@ -0,0 +1,27 @@ + + +ParentNode.children + + + +
+
+
  • 1
  • 2
  • 3
  • 4
+
+ + From 510dd8bf9dd0a304796b3de2b7dbe18d8a261ea8 Mon Sep 17 00:00:00 2001 From: Pierre Tachoire Date: Wed, 13 Dec 2023 16:23:45 +0100 Subject: [PATCH 09/12] dom: imprive children walker --- src/dom/html_collection.zig | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/dom/html_collection.zig b/src/dom/html_collection.zig index f3191ceb..4275ea02 100644 --- a/src/dom/html_collection.zig +++ b/src/dom/html_collection.zig @@ -199,12 +199,6 @@ pub const WalkerChildren = struct { // don't want to go further to find children. if (root == cur.?) return null; - // TODO deinit last. - const last = try parser.nodeLastChild(root); - if (last == cur.?) { - return null; - } - return try parser.nodeNextSibling(cur.?); } }; From be6ba7a1d61e3d773b046ac458a4b9a09f70fe5e Mon Sep 17 00:00:00 2001 From: Pierre Tachoire Date: Wed, 13 Dec 2023 16:23:58 +0100 Subject: [PATCH 10/12] apt: move test on the right place --- tests/wpt/dom/Element-children.html | 58 ------------------- .../Document-getElementsByClassName.html | 0 .../Element-childElement-null-svg.svg | 0 .../Element-childElement-null-xhtml.xhtml | 0 .../Element-childElement-null.html | 0 ...ment-childElementCount-dynamic-add-svg.svg | 0 ...-childElementCount-dynamic-add-xhtml.xhtml | 0 ...Element-childElementCount-dynamic-add.html | 0 ...t-childElementCount-dynamic-remove-svg.svg | 0 ...ildElementCount-dynamic-remove-xhtml.xhtml | 0 ...ment-childElementCount-dynamic-remove.html | 0 .../Element-childElementCount-nochild-svg.svg | 0 ...ment-childElementCount-nochild-xhtml.xhtml | 0 .../Element-childElementCount-nochild.html | 0 .../Element-childElementCount-svg.svg | 0 .../Element-childElementCount-xhtml.xhtml | 0 .../Element-childElementCount.html | 0 ...ement-firstElementChild-entity-xhtml.xhtml | 0 .../Element-firstElementChild-entity.svg | 0 ...lement-firstElementChild-namespace-svg.svg | 0 ...nt-firstElementChild-namespace-xhtml.xhtml | 0 .../Element-firstElementChild-namespace.html | 0 .../Element-firstElementChild-svg.svg | 0 .../Element-firstElementChild-xhtml.xhtml | 0 .../Element-firstElementChild.html | 0 .../Element-lastElementChild-svg.svg | 0 .../Element-lastElementChild-xhtml.xhtml | 0 .../{ => nodes}/Element-lastElementChild.html | 0 28 files changed, 58 deletions(-) delete mode 100644 tests/wpt/dom/Element-children.html rename tests/wpt/dom/{ => nodes}/Document-getElementsByClassName.html (100%) rename tests/wpt/dom/{ => nodes}/Element-childElement-null-svg.svg (100%) rename tests/wpt/dom/{ => nodes}/Element-childElement-null-xhtml.xhtml (100%) rename tests/wpt/dom/{ => nodes}/Element-childElement-null.html (100%) rename tests/wpt/dom/{ => nodes}/Element-childElementCount-dynamic-add-svg.svg (100%) rename tests/wpt/dom/{ => nodes}/Element-childElementCount-dynamic-add-xhtml.xhtml (100%) rename tests/wpt/dom/{ => nodes}/Element-childElementCount-dynamic-add.html (100%) rename tests/wpt/dom/{ => nodes}/Element-childElementCount-dynamic-remove-svg.svg (100%) rename tests/wpt/dom/{ => nodes}/Element-childElementCount-dynamic-remove-xhtml.xhtml (100%) rename tests/wpt/dom/{ => nodes}/Element-childElementCount-dynamic-remove.html (100%) rename tests/wpt/dom/{ => nodes}/Element-childElementCount-nochild-svg.svg (100%) rename tests/wpt/dom/{ => nodes}/Element-childElementCount-nochild-xhtml.xhtml (100%) rename tests/wpt/dom/{ => nodes}/Element-childElementCount-nochild.html (100%) rename tests/wpt/dom/{ => nodes}/Element-childElementCount-svg.svg (100%) rename tests/wpt/dom/{ => nodes}/Element-childElementCount-xhtml.xhtml (100%) rename tests/wpt/dom/{ => nodes}/Element-childElementCount.html (100%) rename tests/wpt/dom/{ => nodes}/Element-firstElementChild-entity-xhtml.xhtml (100%) rename tests/wpt/dom/{ => nodes}/Element-firstElementChild-entity.svg (100%) rename tests/wpt/dom/{ => nodes}/Element-firstElementChild-namespace-svg.svg (100%) rename tests/wpt/dom/{ => nodes}/Element-firstElementChild-namespace-xhtml.xhtml (100%) rename tests/wpt/dom/{ => nodes}/Element-firstElementChild-namespace.html (100%) rename tests/wpt/dom/{ => nodes}/Element-firstElementChild-svg.svg (100%) rename tests/wpt/dom/{ => nodes}/Element-firstElementChild-xhtml.xhtml (100%) rename tests/wpt/dom/{ => nodes}/Element-firstElementChild.html (100%) rename tests/wpt/dom/{ => nodes}/Element-lastElementChild-svg.svg (100%) rename tests/wpt/dom/{ => nodes}/Element-lastElementChild-xhtml.xhtml (100%) rename tests/wpt/dom/{ => nodes}/Element-lastElementChild.html (100%) diff --git a/tests/wpt/dom/Element-children.html b/tests/wpt/dom/Element-children.html deleted file mode 100644 index c0210f96..00000000 --- a/tests/wpt/dom/Element-children.html +++ /dev/null @@ -1,58 +0,0 @@ - -HTMLCollection edge cases - - -
-
- diff --git a/tests/wpt/dom/Document-getElementsByClassName.html b/tests/wpt/dom/nodes/Document-getElementsByClassName.html similarity index 100% rename from tests/wpt/dom/Document-getElementsByClassName.html rename to tests/wpt/dom/nodes/Document-getElementsByClassName.html diff --git a/tests/wpt/dom/Element-childElement-null-svg.svg b/tests/wpt/dom/nodes/Element-childElement-null-svg.svg similarity index 100% rename from tests/wpt/dom/Element-childElement-null-svg.svg rename to tests/wpt/dom/nodes/Element-childElement-null-svg.svg diff --git a/tests/wpt/dom/Element-childElement-null-xhtml.xhtml b/tests/wpt/dom/nodes/Element-childElement-null-xhtml.xhtml similarity index 100% rename from tests/wpt/dom/Element-childElement-null-xhtml.xhtml rename to tests/wpt/dom/nodes/Element-childElement-null-xhtml.xhtml diff --git a/tests/wpt/dom/Element-childElement-null.html b/tests/wpt/dom/nodes/Element-childElement-null.html similarity index 100% rename from tests/wpt/dom/Element-childElement-null.html rename to tests/wpt/dom/nodes/Element-childElement-null.html diff --git a/tests/wpt/dom/Element-childElementCount-dynamic-add-svg.svg b/tests/wpt/dom/nodes/Element-childElementCount-dynamic-add-svg.svg similarity index 100% rename from tests/wpt/dom/Element-childElementCount-dynamic-add-svg.svg rename to tests/wpt/dom/nodes/Element-childElementCount-dynamic-add-svg.svg diff --git a/tests/wpt/dom/Element-childElementCount-dynamic-add-xhtml.xhtml b/tests/wpt/dom/nodes/Element-childElementCount-dynamic-add-xhtml.xhtml similarity index 100% rename from tests/wpt/dom/Element-childElementCount-dynamic-add-xhtml.xhtml rename to tests/wpt/dom/nodes/Element-childElementCount-dynamic-add-xhtml.xhtml diff --git a/tests/wpt/dom/Element-childElementCount-dynamic-add.html b/tests/wpt/dom/nodes/Element-childElementCount-dynamic-add.html similarity index 100% rename from tests/wpt/dom/Element-childElementCount-dynamic-add.html rename to tests/wpt/dom/nodes/Element-childElementCount-dynamic-add.html diff --git a/tests/wpt/dom/Element-childElementCount-dynamic-remove-svg.svg b/tests/wpt/dom/nodes/Element-childElementCount-dynamic-remove-svg.svg similarity index 100% rename from tests/wpt/dom/Element-childElementCount-dynamic-remove-svg.svg rename to tests/wpt/dom/nodes/Element-childElementCount-dynamic-remove-svg.svg diff --git a/tests/wpt/dom/Element-childElementCount-dynamic-remove-xhtml.xhtml b/tests/wpt/dom/nodes/Element-childElementCount-dynamic-remove-xhtml.xhtml similarity index 100% rename from tests/wpt/dom/Element-childElementCount-dynamic-remove-xhtml.xhtml rename to tests/wpt/dom/nodes/Element-childElementCount-dynamic-remove-xhtml.xhtml diff --git a/tests/wpt/dom/Element-childElementCount-dynamic-remove.html b/tests/wpt/dom/nodes/Element-childElementCount-dynamic-remove.html similarity index 100% rename from tests/wpt/dom/Element-childElementCount-dynamic-remove.html rename to tests/wpt/dom/nodes/Element-childElementCount-dynamic-remove.html diff --git a/tests/wpt/dom/Element-childElementCount-nochild-svg.svg b/tests/wpt/dom/nodes/Element-childElementCount-nochild-svg.svg similarity index 100% rename from tests/wpt/dom/Element-childElementCount-nochild-svg.svg rename to tests/wpt/dom/nodes/Element-childElementCount-nochild-svg.svg diff --git a/tests/wpt/dom/Element-childElementCount-nochild-xhtml.xhtml b/tests/wpt/dom/nodes/Element-childElementCount-nochild-xhtml.xhtml similarity index 100% rename from tests/wpt/dom/Element-childElementCount-nochild-xhtml.xhtml rename to tests/wpt/dom/nodes/Element-childElementCount-nochild-xhtml.xhtml diff --git a/tests/wpt/dom/Element-childElementCount-nochild.html b/tests/wpt/dom/nodes/Element-childElementCount-nochild.html similarity index 100% rename from tests/wpt/dom/Element-childElementCount-nochild.html rename to tests/wpt/dom/nodes/Element-childElementCount-nochild.html diff --git a/tests/wpt/dom/Element-childElementCount-svg.svg b/tests/wpt/dom/nodes/Element-childElementCount-svg.svg similarity index 100% rename from tests/wpt/dom/Element-childElementCount-svg.svg rename to tests/wpt/dom/nodes/Element-childElementCount-svg.svg diff --git a/tests/wpt/dom/Element-childElementCount-xhtml.xhtml b/tests/wpt/dom/nodes/Element-childElementCount-xhtml.xhtml similarity index 100% rename from tests/wpt/dom/Element-childElementCount-xhtml.xhtml rename to tests/wpt/dom/nodes/Element-childElementCount-xhtml.xhtml diff --git a/tests/wpt/dom/Element-childElementCount.html b/tests/wpt/dom/nodes/Element-childElementCount.html similarity index 100% rename from tests/wpt/dom/Element-childElementCount.html rename to tests/wpt/dom/nodes/Element-childElementCount.html diff --git a/tests/wpt/dom/Element-firstElementChild-entity-xhtml.xhtml b/tests/wpt/dom/nodes/Element-firstElementChild-entity-xhtml.xhtml similarity index 100% rename from tests/wpt/dom/Element-firstElementChild-entity-xhtml.xhtml rename to tests/wpt/dom/nodes/Element-firstElementChild-entity-xhtml.xhtml diff --git a/tests/wpt/dom/Element-firstElementChild-entity.svg b/tests/wpt/dom/nodes/Element-firstElementChild-entity.svg similarity index 100% rename from tests/wpt/dom/Element-firstElementChild-entity.svg rename to tests/wpt/dom/nodes/Element-firstElementChild-entity.svg diff --git a/tests/wpt/dom/Element-firstElementChild-namespace-svg.svg b/tests/wpt/dom/nodes/Element-firstElementChild-namespace-svg.svg similarity index 100% rename from tests/wpt/dom/Element-firstElementChild-namespace-svg.svg rename to tests/wpt/dom/nodes/Element-firstElementChild-namespace-svg.svg diff --git a/tests/wpt/dom/Element-firstElementChild-namespace-xhtml.xhtml b/tests/wpt/dom/nodes/Element-firstElementChild-namespace-xhtml.xhtml similarity index 100% rename from tests/wpt/dom/Element-firstElementChild-namespace-xhtml.xhtml rename to tests/wpt/dom/nodes/Element-firstElementChild-namespace-xhtml.xhtml diff --git a/tests/wpt/dom/Element-firstElementChild-namespace.html b/tests/wpt/dom/nodes/Element-firstElementChild-namespace.html similarity index 100% rename from tests/wpt/dom/Element-firstElementChild-namespace.html rename to tests/wpt/dom/nodes/Element-firstElementChild-namespace.html diff --git a/tests/wpt/dom/Element-firstElementChild-svg.svg b/tests/wpt/dom/nodes/Element-firstElementChild-svg.svg similarity index 100% rename from tests/wpt/dom/Element-firstElementChild-svg.svg rename to tests/wpt/dom/nodes/Element-firstElementChild-svg.svg diff --git a/tests/wpt/dom/Element-firstElementChild-xhtml.xhtml b/tests/wpt/dom/nodes/Element-firstElementChild-xhtml.xhtml similarity index 100% rename from tests/wpt/dom/Element-firstElementChild-xhtml.xhtml rename to tests/wpt/dom/nodes/Element-firstElementChild-xhtml.xhtml diff --git a/tests/wpt/dom/Element-firstElementChild.html b/tests/wpt/dom/nodes/Element-firstElementChild.html similarity index 100% rename from tests/wpt/dom/Element-firstElementChild.html rename to tests/wpt/dom/nodes/Element-firstElementChild.html diff --git a/tests/wpt/dom/Element-lastElementChild-svg.svg b/tests/wpt/dom/nodes/Element-lastElementChild-svg.svg similarity index 100% rename from tests/wpt/dom/Element-lastElementChild-svg.svg rename to tests/wpt/dom/nodes/Element-lastElementChild-svg.svg diff --git a/tests/wpt/dom/Element-lastElementChild-xhtml.xhtml b/tests/wpt/dom/nodes/Element-lastElementChild-xhtml.xhtml similarity index 100% rename from tests/wpt/dom/Element-lastElementChild-xhtml.xhtml rename to tests/wpt/dom/nodes/Element-lastElementChild-xhtml.xhtml diff --git a/tests/wpt/dom/Element-lastElementChild.html b/tests/wpt/dom/nodes/Element-lastElementChild.html similarity index 100% rename from tests/wpt/dom/Element-lastElementChild.html rename to tests/wpt/dom/nodes/Element-lastElementChild.html From 314d2dae806becbd5bb4fe42577259425c55e86b Mon Sep 17 00:00:00 2001 From: Pierre Tachoire Date: Fri, 15 Dec 2023 13:54:52 +0100 Subject: [PATCH 11/12] typo fix Co-authored-by: Francis Bouvier --- src/dom/html_collection.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dom/html_collection.zig b/src/dom/html_collection.zig index 4275ea02..c51e4133 100644 --- a/src/dom/html_collection.zig +++ b/src/dom/html_collection.zig @@ -191,7 +191,7 @@ pub const WalkerDepthFirst = struct { // WalkerChildren iterates over the root's children only. pub const WalkerChildren = struct { pub fn get_next(_: WalkerChildren, root: *parser.Node, cur: ?*parser.Node) !?*parser.Node { - // On wlak start, we return the first root's child. + // On walk start, we return the first root's child. if (cur == null) return try parser.nodeFirstChild(root); // If cur is root, then return null. From bedac0a2c5bcc46f4d04a9bec9b1bb2ad2d3e423 Mon Sep 17 00:00:00 2001 From: Pierre Tachoire Date: Fri, 15 Dec 2023 13:54:10 +0100 Subject: [PATCH 12/12] html_collection: explicitely list switch cases --- src/dom/html_collection.zig | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/dom/html_collection.zig b/src/dom/html_collection.zig index c51e4133..da4bf2f6 100644 --- a/src/dom/html_collection.zig +++ b/src/dom/html_collection.zig @@ -19,14 +19,16 @@ const Matcher = union(enum) { pub fn match(self: Matcher, node: *parser.Node) !bool { switch (self) { inline .matchTrue => return true, - inline else => |case| return case.match(node), + inline .matchByTagName => |case| return case.match(node), + inline .matchByClassName => |case| return case.match(node), } } pub fn deinit(self: Matcher, alloc: std.mem.Allocator) void { switch (self) { - .matchTrue => return, - inline else => |case| return case.deinit(alloc), + inline .matchTrue => return, + inline .matchByTagName => |case| return case.deinit(alloc), + inline .matchByClassName => |case| return case.deinit(alloc), } } };