diff --git a/src/dom/document.zig b/src/dom/document.zig index b889169b..f869f8e5 100644 --- a/src/dom/document.zig +++ b/src/dom/document.zig @@ -7,7 +7,8 @@ const Case = jsruntime.test_utils.Case; const checkCases = jsruntime.test_utils.checkCases; const Node = @import("node.zig").Node; -const HTMLCollection = @import("html_collection.zig").HTMLCollection; + +const collection = @import("html_collection.zig"); const Element = @import("element.zig").Element; const ElementUnion = @import("element.zig").Union; @@ -49,12 +50,9 @@ pub const Document = struct { // the spec changed to return an HTMLCollection instead. // That's why we reimplemented getElementsByTagName by using an // HTMLCollection in zig here. - pub fn _getElementsByTagName(self: *parser.Document, tag_name: []const u8) HTMLCollection { + pub fn _getElementsByTagName(self: *parser.Document, allocator: std.mem.Allocator, tag_name: []const u8) !collection.HTMLCollection { const root = parser.documentGetDocumentElement(self); - return HTMLCollection{ - .root = parser.elementToNode(root), - .match = tag_name, - }; + return collection.HTMLCollectionByTagName(allocator, parser.elementToNode(root), tag_name); } }; diff --git a/src/dom/html_collection.zig b/src/dom/html_collection.zig index 10cfb78f..c9e02b22 100644 --- a/src/dom/html_collection.zig +++ b/src/dom/html_collection.zig @@ -5,11 +5,59 @@ const parser = @import("../netsurf.zig"); const jsruntime = @import("jsruntime"); const Case = jsruntime.test_utils.Case; const checkCases = jsruntime.test_utils.checkCases; +const generate = @import("../generate.zig"); const utils = @import("utils.z"); const Element = @import("element.zig").Element; const Union = @import("element.zig").Union; +const Match = union(enum) { + matchByTagName: MatchByTagName, + + pub fn match(self: Match, node: *parser.Node) bool { + switch (self) { + inline else => |case| return case.match(node), + } + } + + pub fn deinit(self: Match, allocator: std.mem.Allocator) void { + switch (self) { + inline else => |case| case.deinit(allocator), + } + } +}; + +pub const MatchByTagName = struct { + // tag is used to select node against their name. + // tag comparison is case insensitive. + tag: []const u8, + is_wildcard: bool, + + fn init(allocator: std.mem.Allocator, tag_name: []const u8) !MatchByTagName { + return MatchByTagName{ + .tag = try std.ascii.allocUpperString(allocator, tag_name), + .is_wildcard = std.mem.eql(u8, tag_name, "*"), + }; + } + + fn deinit(self: *MatchByTagName, allocator: std.mem.Allocator) void { + allocator.free(self.tag); + } + + pub fn match(self: MatchByTagName, node: *parser.Node) bool { + return self.is_wildcard or std.mem.eql(u8, self.tag, parser.nodeName(node)); + } +}; + +pub fn HTMLCollectionByTagName(allocator: std.mem.Allocator, root: *parser.Node, tag_name: []const u8) !HTMLCollection { + return HTMLCollection{ + .root = root, + .match = Match{ + .matchByTagName = try MatchByTagName.init(allocator, tag_name), + }, + }; +} + // 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. @@ -17,10 +65,9 @@ const Union = @import("element.zig").Union; pub const HTMLCollection = struct { pub const mem_guarantied = true; + match: Match, + root: *parser.Node, - // match is used to select node against their name. - // match comparison is case insensitive. - match: []const u8, // save a state for the collection to improve the _item speed. cur_idx: ?u32 = undefined, @@ -76,20 +123,15 @@ pub const HTMLCollection = struct { /// 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, allocator: std.mem.Allocator) !u32 { + pub fn get_length(self: *HTMLCollection) u32 { var len: u32 = 0; var node: *parser.Node = self.root; var ntype: parser.NodeType = undefined; - const imatch = try std.ascii.allocUpperString(allocator, self.match); - defer allocator.free(imatch); - - const is_wildcard = std.mem.eql(u8, self.match, "*"); - while (true) { ntype = parser.nodeType(node); if (ntype == .element) { - if (is_wildcard or std.mem.eql(u8, imatch, parser.nodeName(node))) { + if (self.match.match(node)) { len += 1; } } @@ -100,26 +142,21 @@ pub const HTMLCollection = struct { return len; } - pub fn _item(self: *HTMLCollection, allocator: std.mem.Allocator, index: u32) !?Union { + pub fn _item(self: *HTMLCollection, index: u32) ?Union { var i: u32 = 0; var node: *parser.Node = self.root; var ntype: parser.NodeType = undefined; - const is_wildcard = std.mem.eql(u8, self.match, "*"); - // 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.?; } - const imatch = try std.ascii.allocUpperString(allocator, self.match); - defer allocator.free(imatch); - while (true) { ntype = parser.nodeType(node); if (ntype == .element) { - if (is_wildcard or std.mem.eql(u8, imatch, parser.nodeName(node))) { + if (self.match.match(node)) { // check if we found the searched element. if (i == index) { // save the current state @@ -140,7 +177,7 @@ pub const HTMLCollection = struct { return null; } - pub fn _namedItem(self: *HTMLCollection, allocator: std.mem.Allocator, name: []const u8) !?Union { + pub fn _namedItem(self: *HTMLCollection, name: []const u8) ?Union { if (name.len == 0) { return null; } @@ -148,15 +185,10 @@ pub const HTMLCollection = struct { var node: *parser.Node = self.root; var ntype: parser.NodeType = undefined; - const is_wildcard = std.mem.eql(u8, self.match, "*"); - - const imatch = try std.ascii.allocUpperString(allocator, self.match); - defer allocator.free(imatch); - while (true) { ntype = parser.nodeType(node); if (ntype == .element) { - if (is_wildcard or std.mem.eql(u8, imatch, parser.nodeName(node))) { + if (self.match.match(node)) { const elem = @as(*parser.Element, @ptrCast(node)); var attr = parser.elementGetAttribute(elem, "id");