From badfe39a3d0747cfc4054542373d251693d4cad6 Mon Sep 17 00:00:00 2001 From: Karl Seguin Date: Thu, 12 Feb 2026 12:31:05 +0800 Subject: [PATCH] Refactor common Document and Element methods into Node --- src/browser/webapi/Document.zig | 56 +++-------------------------- src/browser/webapi/Element.zig | 56 +++-------------------------- src/browser/webapi/Node.zig | 63 +++++++++++++++++++++++++++++++++ 3 files changed, 71 insertions(+), 104 deletions(-) diff --git a/src/browser/webapi/Document.zig b/src/browser/webapi/Document.zig index 08b35ae6..ff6f71d8 100644 --- a/src/browser/webapi/Document.zig +++ b/src/browser/webapi/Document.zig @@ -219,64 +219,16 @@ pub fn getElementById(self: *Document, id: []const u8, page: *Page) ?*Element { return null; } -const GetElementsByTagNameResult = union(enum) { - tag: collections.NodeLive(.tag), - tag_name: collections.NodeLive(.tag_name), - all_elements: collections.NodeLive(.all_elements), -}; -pub fn getElementsByTagName(self: *Document, tag_name: []const u8, page: *Page) !GetElementsByTagNameResult { - if (tag_name.len > 256) { - // 256 seems generous. - return error.InvalidTagName; - } - - if (std.mem.eql(u8, tag_name, "*")) { - return .{ - .all_elements = collections.NodeLive(.all_elements).init(self.asNode(), {}, page), - }; - } - - const lower = std.ascii.lowerString(&page.buf, tag_name); - if (Node.Element.Tag.parseForMatch(lower)) |known| { - // optimized for known tag names, comparis - return .{ - .tag = collections.NodeLive(.tag).init(self.asNode(), known, page), - }; - } - - const arena = page.arena; - const filter = try String.init(arena, lower, .{}); - return .{ .tag_name = collections.NodeLive(.tag_name).init(self.asNode(), filter, page) }; +pub fn getElementsByTagName(self: *Document, tag_name: []const u8, page: *Page) !Node.GetElementsByTagNameResult { + return self.asNode().getElementsByTagName(tag_name, page); } pub fn getElementsByTagNameNS(self: *Document, namespace: ?[]const u8, local_name: []const u8, page: *Page) !collections.NodeLive(.tag_name_ns) { - if (local_name.len > 256) { - return error.InvalidTagName; - } - - // Parse namespace - "*" means wildcard (null), null means Element.Namespace.null - const ns: ?Element.Namespace = if (namespace) |ns_str| - if (std.mem.eql(u8, ns_str, "*")) null else Element.Namespace.parse(ns_str) - else - Element.Namespace.null; - - return collections.NodeLive(.tag_name_ns).init(self.asNode(), .{ - .namespace = ns, - .local_name = try String.init(page.arena, local_name, .{}), - }, page); + return self.asNode().getElementsByTagNameNS(namespace, local_name, page); } pub fn getElementsByClassName(self: *Document, class_name: []const u8, page: *Page) !collections.NodeLive(.class_name) { - const arena = page.arena; - - // Parse space-separated class names - var class_names: std.ArrayList([]const u8) = .empty; - var it = std.mem.tokenizeAny(u8, class_name, "\t\n\x0C\r "); - while (it.next()) |name| { - try class_names.append(arena, try page.dupeString(name)); - } - - return collections.NodeLive(.class_name).init(self.asNode(), class_names.items, page); + return self.asNode().getElementsByClassName(class_name, page); } pub fn getElementsByName(self: *Document, name: []const u8, page: *Page) !collections.NodeLive(.name) { diff --git a/src/browser/webapi/Element.zig b/src/browser/webapi/Element.zig index 679a78f8..a118ea11 100644 --- a/src/browser/webapi/Element.zig +++ b/src/browser/webapi/Element.zig @@ -1105,64 +1105,16 @@ fn calculateSiblingPosition(node: *Node) f64 { return position * 5.0; // 5px per node } -const GetElementsByTagNameResult = union(enum) { - tag: collections.NodeLive(.tag), - tag_name: collections.NodeLive(.tag_name), - all_elements: collections.NodeLive(.all_elements), -}; -pub fn getElementsByTagName(self: *Element, tag_name: []const u8, page: *Page) !GetElementsByTagNameResult { - if (tag_name.len > 256) { - // 256 seems generous. - return error.InvalidTagName; - } - - if (std.mem.eql(u8, tag_name, "*")) { - return .{ - .all_elements = collections.NodeLive(.all_elements).init(self.asNode(), {}, page), - }; - } - - const lower = std.ascii.lowerString(&page.buf, tag_name); - if (Tag.parseForMatch(lower)) |known| { - // optimized for known tag names - return .{ - .tag = collections.NodeLive(.tag).init(self.asNode(), known, page), - }; - } - - const arena = page.arena; - const filter = try String.init(arena, lower, .{}); - return .{ .tag_name = collections.NodeLive(.tag_name).init(self.asNode(), filter, page) }; +pub fn getElementsByTagName(self: *Element, tag_name: []const u8, page: *Page) !Node.GetElementsByTagNameResult { + return self.asNode().getElementsByTagName(tag_name, page); } pub fn getElementsByTagNameNS(self: *Element, namespace: ?[]const u8, local_name: []const u8, page: *Page) !collections.NodeLive(.tag_name_ns) { - if (local_name.len > 256) { - return error.InvalidTagName; - } - - // Parse namespace - "*" means wildcard (null), null means Namespace.null - const ns: ?Namespace = if (namespace) |ns_str| - if (std.mem.eql(u8, ns_str, "*")) null else Namespace.parse(ns_str) - else - Namespace.null; - - return collections.NodeLive(.tag_name_ns).init(self.asNode(), .{ - .namespace = ns, - .local_name = try String.init(page.arena, local_name, .{}), - }, page); + return self.asNode().getElementsByTagNameNS(namespace, local_name, page); } pub fn getElementsByClassName(self: *Element, class_name: []const u8, page: *Page) !collections.NodeLive(.class_name) { - const arena = page.arena; - - // Parse space-separated class names - var class_names: std.ArrayList([]const u8) = .empty; - var it = std.mem.tokenizeAny(u8, class_name, "\t\n\x0C\r "); - while (it.next()) |name| { - try class_names.append(arena, try page.dupeString(name)); - } - - return collections.NodeLive(.class_name).init(self.asNode(), class_names.items, page); + return self.asNode().getElementsByClassName(class_name, page); } pub fn clone(self: *Element, deep: bool, page: *Page) !*Node { diff --git a/src/browser/webapi/Node.zig b/src/browser/webapi/Node.zig index 7482e042..e93b9b25 100644 --- a/src/browser/webapi/Node.zig +++ b/src/browser/webapi/Node.zig @@ -845,6 +845,69 @@ fn _normalize(self: *Node, allocator: Allocator, buffer: *std.ArrayList(u8), pag } } +pub const GetElementsByTagNameResult = union(enum) { + tag: collections.NodeLive(.tag), + tag_name: collections.NodeLive(.tag_name), + all_elements: collections.NodeLive(.all_elements), +}; +// Not exposed in the WebAPI, but used by both Element and Document +pub fn getElementsByTagName(self: *Node, tag_name: []const u8, page: *Page) !GetElementsByTagNameResult { + if (tag_name.len > 256) { + // 256 seems generous. + return error.InvalidTagName; + } + + if (std.mem.eql(u8, tag_name, "*")) { + return .{ + .all_elements = collections.NodeLive(.all_elements).init(self, {}, page), + }; + } + + const lower = std.ascii.lowerString(&page.buf, tag_name); + if (Node.Element.Tag.parseForMatch(lower)) |known| { + // optimized for known tag names, comparis + return .{ + .tag = collections.NodeLive(.tag).init(self, known, page), + }; + } + + const arena = page.arena; + const filter = try String.init(arena, lower, .{}); + return .{ .tag_name = collections.NodeLive(.tag_name).init(self, filter, page) }; +} + +// Not exposed in the WebAPI, but used by both Element and Document +pub fn getElementsByTagNameNS(self: *Node, namespace: ?[]const u8, local_name: []const u8, page: *Page) !collections.NodeLive(.tag_name_ns) { + if (local_name.len > 256) { + return error.InvalidTagName; + } + + // Parse namespace - "*" means wildcard (null), null means Element.Namespace.null + const ns: ?Element.Namespace = if (namespace) |ns_str| + if (std.mem.eql(u8, ns_str, "*")) null else Element.Namespace.parse(ns_str) + else + Element.Namespace.null; + + return collections.NodeLive(.tag_name_ns).init(self, .{ + .namespace = ns, + .local_name = try String.init(page.arena, local_name, .{}), + }, page); +} + +// Not exposed in the WebAPI, but used by both Element and Document +pub fn getElementsByClassName(self: *Node, class_name: []const u8, page: *Page) !collections.NodeLive(.class_name) { + const arena = page.arena; + + // Parse space-separated class names + var class_names: std.ArrayList([]const u8) = .empty; + var it = std.mem.tokenizeAny(u8, class_name, "\t\n\x0C\r "); + while (it.next()) |name| { + try class_names.append(arena, try page.dupeString(name)); + } + + return collections.NodeLive(.class_name).init(self, class_names.items, page); +} + // Writes a JSON representation of the node and its children pub fn jsonStringify(self: *const Node, writer: *std.json.Stringify) !void { // stupid json api requires this to be const,