diff --git a/src/dom/element.zig b/src/dom/element.zig index 8ab9d3c7..936a3813 100644 --- a/src/dom/element.zig +++ b/src/dom/element.zig @@ -9,6 +9,7 @@ const checkCases = jsruntime.test_utils.checkCases; const collection = @import("html_collection.zig"); const Node = @import("node.zig").Node; +const NodeList = @import("nodelist.zig").NodeList; const HTMLElem = @import("../html/elements.zig"); pub const Union = @import("../html/elements.zig").Union; diff --git a/src/dom/node.zig b/src/dom/node.zig index 29c80239..3144ea4e 100644 --- a/src/dom/node.zig +++ b/src/dom/node.zig @@ -14,6 +14,7 @@ const EventTarget = @import("event_target.zig").EventTarget; const Attr = @import("attribute.zig").Attr; const CData = @import("character_data.zig"); const Element = @import("element.zig").Element; +const NodeList = @import("nodelist.zig").NodeList; const Document = @import("document.zig").Document; const DocumentType = @import("document_type.zig").DocumentType; const DocumentFragment = @import("document_fragment.zig").DocumentFragment; @@ -193,8 +194,15 @@ pub const Node = struct { return try parser.nodeHasChildNodes(self); } - pub fn get_childNodes(self: *parser.Node) !*parser.NodeList { - return try parser.nodeGetChildNodes(self); + pub fn get_childNodes(self: *parser.Node, alloc: std.mem.Allocator) !*NodeList { + const list = try NodeList.init(alloc); + errdefer list.deinit(alloc); + + var n = try parser.nodeFirstChild(self) orelse return list; + while (true) { + try list.append(n); + n = try parser.nodeNextSibling(n) orelse return list; + } } pub fn _insertBefore(self: *parser.Node, new_node: *parser.Node, ref_node: *parser.Node) !*parser.Node { @@ -246,6 +254,8 @@ pub const Node = struct { const res = try parser.nodeReplaceChild(self, new_child, old_child); return try Node.toInterface(res); } + + pub fn deinit(_: *parser.Node, _: std.mem.Allocator) void {} }; // Tests diff --git a/src/dom/nodelist.zig b/src/dom/nodelist.zig index 4d6ff51b..b663ec4b 100644 --- a/src/dom/nodelist.zig +++ b/src/dom/nodelist.zig @@ -3,30 +3,73 @@ const std = @import("std"); const parser = @import("../netsurf.zig"); const jsruntime = @import("jsruntime"); +const Case = jsruntime.test_utils.Case; +const checkCases = jsruntime.test_utils.checkCases; const NodeUnion = @import("node.zig").Union; const Node = @import("node.zig").Node; const DOMException = @import("exceptions.zig").DOMException; +// Nodelist is implemented in pure Zig b/c libdom's NodeList doesn't allow to +// append nodes. // WEB IDL https://dom.spec.whatwg.org/#nodelist pub const NodeList = struct { - pub const Self = parser.NodeList; pub const mem_guarantied = true; - pub const Exception = DOMException; - pub fn get_length(self: *parser.NodeList) !u32 { - return try parser.nodeListLength(self); + const NodesArrayList = std.ArrayList(*parser.Node); + + nodes: NodesArrayList, + + pub fn init(alloc: std.mem.Allocator) !*NodeList { + const list = try alloc.create(NodeList); + list.* = NodeList{ + .nodes = NodesArrayList.init(alloc), + }; + + return list; } - pub fn _item(self: *parser.NodeList, index: u32) !?NodeUnion { - const n = try parser.nodeListItem(self, index); - if (n == null) return null; - return try Node.toInterface(n.?); + pub fn deinit(self: *NodeList, alloc: std.mem.Allocator) void { + // TODO unref all nodes + self.nodes.deinit(); + alloc.destroy(self); + } + + pub fn append(self: *NodeList, node: *parser.Node) !void { + try self.nodes.append(node); + } + + pub fn get_length(self: *NodeList) u32 { + return @intCast(self.nodes.items.len); + } + + pub fn _item(self: *NodeList, index: u32) !?NodeUnion { + if (index >= self.nodes.items.len) { + return null; + } + + const n = self.nodes.items[index]; + return try Node.toInterface(n); } // TODO _symbol_iterator // TODO implement postAttach }; + +// Tests +// ----- + +pub fn testExecFn( + _: std.mem.Allocator, + js_env: *jsruntime.Env, + comptime _: []jsruntime.API, +) !void { + var childnodes = [_]Case{ + .{ .src = "let list = document.getElementById('content').childNodes", .ex = "undefined" }, + .{ .src = "list.length", .ex = "9" }, + }; + try checkCases(js_env, &childnodes); +} diff --git a/src/netsurf.zig b/src/netsurf.zig index f04eb4d9..a2b268dc 100644 --- a/src/netsurf.zig +++ b/src/netsurf.zig @@ -360,26 +360,6 @@ pub const NodeListType = enum(c_uint) { query = 5, }; -// create reference to external libdom private function manually instead of -// using cimport. -// zig translate-c is unable to generate this declaration, idk why. -pub extern fn _dom_nodelist_create( - document: ?*Document, - nltype: c_uint, - owner: ?*Node, - tagname: [*c]String, - ns: [*c]String, - localname: [*c]String, - list: [*c]?*NodeList, -) DOMException; - -pub fn nodeListCreate(doc: ?*Document, nltype: NodeListType, root: *Node) !*NodeList { - var res: ?*NodeList = undefined; - const err = _dom_nodelist_create(doc, @intFromEnum(nltype), root, null, null, null, &res); - try DOMErr(err); - return res.?; -} - pub fn nodeListLength(nodeList: *NodeList) !u32 { var ln: u32 = undefined; const err = c.dom_nodelist_get_length(nodeList, &ln); @@ -671,13 +651,6 @@ pub fn nodeHasChildNodes(node: *Node) !bool { return res; } -pub fn nodeGetChildNodes(node: *Node) !*NodeList { - var res: ?*NodeList = undefined; - const err = nodeVtable(node).dom_node_get_child_nodes.?(node, &res); - try DOMErr(err); - return res.?; -} - pub fn nodeInsertBefore(node: *Node, new_node: *Node, ref_node: *Node) !*Node { var res: ?*Node = undefined; const err = nodeVtable(node).dom_node_insert_before.?(node, new_node, ref_node, &res); diff --git a/src/run_tests.zig b/src/run_tests.zig index 681e96c0..0f541c79 100644 --- a/src/run_tests.zig +++ b/src/run_tests.zig @@ -17,6 +17,7 @@ const DOMExceptionTestExecFn = @import("dom/exceptions.zig").testExecFn; const DOMImplementationExecFn = @import("dom/implementation.zig").testExecFn; const NamedNodeMapExecFn = @import("dom/namednodemap.zig").testExecFn; const DOMTokenListExecFn = @import("dom/token_list.zig").testExecFn; +const NodeListTestExecFn = @import("dom/nodelist.zig").testExecFn; var doc: *parser.DocumentHTML = undefined; @@ -65,6 +66,7 @@ fn testsAllExecFn( DOMImplementationExecFn, NamedNodeMapExecFn, DOMTokenListExecFn, + NodeListTestExecFn, }; inline for (testFns) |testFn| {