diff --git a/src/cdp/dom.zig b/src/cdp/dom.zig index eee737d5..3a4ceb66 100644 --- a/src/cdp/dom.zig +++ b/src/cdp/dom.zig @@ -24,6 +24,7 @@ const cdp = @import("cdp.zig"); const result = cdp.result; const IncomingMessage = @import("msg.zig").IncomingMessage; const Input = @import("msg.zig").Input; +const css = @import("../dom/css.zig"); const parser = @import("netsurf"); @@ -32,6 +33,7 @@ const log = std.log.scoped(.cdp); const Methods = enum { enable, getDocument, + performSearch, }; pub fn dom( @@ -46,6 +48,7 @@ pub fn dom( return switch (method) { .enable => enable(alloc, msg, ctx), .getDocument => getDocument(alloc, msg, ctx), + .performSearch => performSearch(alloc, msg, ctx), }; } @@ -62,6 +65,36 @@ fn enable( return result(alloc, input.id, null, null, input.sessionId); } +// NodeList references tree nodes with an array id. +pub const NodeList = struct { + coll: List, + + const List = std.ArrayList(*parser.Node); + + pub fn init(alloc: std.mem.Allocator) NodeList { + return .{ + .coll = List.init(alloc), + }; + } + + pub fn deinit(self: *NodeList) void { + self.coll.deinit(); + } + + pub fn reset(self: *NodeList) void { + self.coll.clearAndFree(); + } + + pub fn set(self: *NodeList, node: *parser.Node) !NodeId { + for (self.coll.items, 0..) |n, i| { + if (n == node) return @intCast(i); + } + + try self.coll.append(node); + return @intCast(self.coll.items.len); + } +}; + const NodeId = u32; const Node = struct { @@ -80,13 +113,13 @@ const Node = struct { compatibilityMode: []const u8 = "NoQuirksMode", isScrollable: bool = false, - fn init(n: *parser.Node) !Node { + fn init(n: *parser.Node, id: NodeId) !Node { const children = try parser.nodeGetChildNodes(n); const ln = try parser.nodeListLength(children); return .{ - .nodeId = 1, - .backendNodeId = 1, + .nodeId = id, + .backendNodeId = id, .nodeType = @intFromEnum(try parser.nodeType(n)), .nodeName = try parser.nodeName(n), .localName = try parser.nodeLocalName(n), @@ -117,16 +150,90 @@ fn getDocument( if (page.doc == null) return error.NoDocument; - const root = try parser.documentGetDocumentElement(page.doc.?) orelse { - return error.NoRoot; - }; + const node = parser.documentToNode(page.doc.?); + const id = try ctx.state.nodelist.set(node); // output const Resp = struct { root: Node, }; const resp: Resp = .{ - .root = try Node.init(parser.elementToNode(root)), + .root = try Node.init(node, id), + }; + + return result(alloc, input.id, Resp, resp, input.sessionId); +} + +pub const NodeSearch = struct { + coll: List, + name: []u8, + alloc: std.mem.Allocator, + + var count: u8 = 0; + + const List = std.ArrayListUnmanaged(NodeId); + + pub fn initCapacity(alloc: std.mem.Allocator, ln: usize) !NodeSearch { + count += 1; + + return .{ + .alloc = alloc, + .coll = try List.initCapacity(alloc, ln), + .name = try std.fmt.allocPrint(alloc, "{d}", .{count}), + }; + } + + pub fn deinit(self: *NodeSearch) void { + self.coll.deinit(self.alloc); + self.alloc.free(self.name); + } + + pub fn append(self: *NodeSearch, id: NodeId) !void { + try self.coll.append(self.alloc, id); + } +}; +pub const NodeSearchList = std.ArrayList(NodeSearch); + +// https://chromedevtools.github.io/devtools-protocol/tot/DOM/#method-performSearch +fn performSearch( + alloc: std.mem.Allocator, + msg: *IncomingMessage, + ctx: *Ctx, +) ![]const u8 { + // input + const Params = struct { + query: []const u8, + includeUserAgentShadowDOM: ?bool = null, + }; + const input = try Input(Params).get(alloc, msg); + defer input.deinit(); + std.debug.assert(input.sessionId != null); + log.debug("Req > id {d}, method {s}", .{ input.id, "DOM.performSearch" }); + + // retrieve the root node + const page = ctx.browser.currentPage() orelse return error.NoPage; + + if (page.doc == null) return error.NoDocument; + + const list = try css.querySelectorAll(alloc, parser.documentToNode(page.doc.?), input.params.query); + const ln = list.nodes.items.len; + var ns = try NodeSearch.initCapacity(alloc, ln); + + for (list.nodes.items) |n| { + const id = try ctx.state.nodelist.set(n); + try ns.append(id); + } + + try ctx.state.nodesearchlist.append(ns); + + // output + const Resp = struct { + searchId: []const u8, + resultCount: u32, + }; + const resp: Resp = .{ + .searchId = ns.name, + .resultCount = @intCast(ln), }; return result(alloc, input.id, Resp, resp, input.sessionId);