Skip to content

Commit 4e4a8f1

Browse files
committed
cdp: implement DOM.performSearch
1 parent 39b3786 commit 4e4a8f1

File tree

1 file changed

+114
-7
lines changed

1 file changed

+114
-7
lines changed

src/cdp/dom.zig

Lines changed: 114 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ const cdp = @import("cdp.zig");
2424
const result = cdp.result;
2525
const IncomingMessage = @import("msg.zig").IncomingMessage;
2626
const Input = @import("msg.zig").Input;
27+
const css = @import("../dom/css.zig");
2728

2829
const parser = @import("netsurf");
2930

@@ -32,6 +33,7 @@ const log = std.log.scoped(.cdp);
3233
const Methods = enum {
3334
enable,
3435
getDocument,
36+
performSearch,
3537
};
3638

3739
pub fn dom(
@@ -46,6 +48,7 @@ pub fn dom(
4648
return switch (method) {
4749
.enable => enable(alloc, msg, ctx),
4850
.getDocument => getDocument(alloc, msg, ctx),
51+
.performSearch => performSearch(alloc, msg, ctx),
4952
};
5053
}
5154

@@ -62,6 +65,36 @@ fn enable(
6265
return result(alloc, input.id, null, null, input.sessionId);
6366
}
6467

68+
// NodeList references tree nodes with an array id.
69+
pub const NodeList = struct {
70+
coll: List,
71+
72+
const List = std.ArrayList(*parser.Node);
73+
74+
pub fn init(alloc: std.mem.Allocator) NodeList {
75+
return .{
76+
.coll = List.init(alloc),
77+
};
78+
}
79+
80+
pub fn deinit(self: *NodeList) void {
81+
self.coll.deinit();
82+
}
83+
84+
pub fn reset(self: *NodeList) void {
85+
self.coll.clearAndFree();
86+
}
87+
88+
pub fn set(self: *NodeList, node: *parser.Node) !NodeId {
89+
for (self.coll.items, 0..) |n, i| {
90+
if (n == node) return @intCast(i);
91+
}
92+
93+
try self.coll.append(node);
94+
return @intCast(self.coll.items.len);
95+
}
96+
};
97+
6598
const NodeId = u32;
6699

67100
const Node = struct {
@@ -80,13 +113,13 @@ const Node = struct {
80113
compatibilityMode: []const u8 = "NoQuirksMode",
81114
isScrollable: bool = false,
82115

83-
fn init(n: *parser.Node) !Node {
116+
fn init(n: *parser.Node, id: NodeId) !Node {
84117
const children = try parser.nodeGetChildNodes(n);
85118
const ln = try parser.nodeListLength(children);
86119

87120
return .{
88-
.nodeId = 1,
89-
.backendNodeId = 1,
121+
.nodeId = id,
122+
.backendNodeId = id,
90123
.nodeType = @intFromEnum(try parser.nodeType(n)),
91124
.nodeName = try parser.nodeName(n),
92125
.localName = try parser.nodeLocalName(n),
@@ -117,16 +150,90 @@ fn getDocument(
117150

118151
if (page.doc == null) return error.NoDocument;
119152

120-
const root = try parser.documentGetDocumentElement(page.doc.?) orelse {
121-
return error.NoRoot;
122-
};
153+
const node = parser.documentToNode(page.doc.?);
154+
const id = try ctx.state.nodelist.set(node);
123155

124156
// output
125157
const Resp = struct {
126158
root: Node,
127159
};
128160
const resp: Resp = .{
129-
.root = try Node.init(parser.elementToNode(root)),
161+
.root = try Node.init(node, id),
162+
};
163+
164+
return result(alloc, input.id, Resp, resp, input.sessionId);
165+
}
166+
167+
pub const NodeSearch = struct {
168+
coll: List,
169+
name: []u8,
170+
alloc: std.mem.Allocator,
171+
172+
var count: u8 = 0;
173+
174+
const List = std.ArrayListUnmanaged(NodeId);
175+
176+
pub fn initCapacity(alloc: std.mem.Allocator, ln: usize) !NodeSearch {
177+
count += 1;
178+
179+
return .{
180+
.alloc = alloc,
181+
.coll = try List.initCapacity(alloc, ln),
182+
.name = try std.fmt.allocPrint(alloc, "{d}", .{count}),
183+
};
184+
}
185+
186+
pub fn deinit(self: *NodeSearch) void {
187+
self.coll.deinit(self.alloc);
188+
self.alloc.free(self.name);
189+
}
190+
191+
pub fn append(self: *NodeSearch, id: NodeId) !void {
192+
try self.coll.append(self.alloc, id);
193+
}
194+
};
195+
pub const NodeSearchList = std.ArrayList(NodeSearch);
196+
197+
// https://chromedevtools.github.io/devtools-protocol/tot/DOM/#method-performSearch
198+
fn performSearch(
199+
alloc: std.mem.Allocator,
200+
msg: *IncomingMessage,
201+
ctx: *Ctx,
202+
) ![]const u8 {
203+
// input
204+
const Params = struct {
205+
query: []const u8,
206+
includeUserAgentShadowDOM: ?bool = null,
207+
};
208+
const input = try Input(Params).get(alloc, msg);
209+
defer input.deinit();
210+
std.debug.assert(input.sessionId != null);
211+
log.debug("Req > id {d}, method {s}", .{ input.id, "DOM.performSearch" });
212+
213+
// retrieve the root node
214+
const page = ctx.browser.currentPage() orelse return error.NoPage;
215+
216+
if (page.doc == null) return error.NoDocument;
217+
218+
const list = try css.querySelectorAll(alloc, parser.documentToNode(page.doc.?), input.params.query);
219+
const ln = list.nodes.items.len;
220+
var ns = try NodeSearch.initCapacity(alloc, ln);
221+
222+
for (list.nodes.items) |n| {
223+
const id = try ctx.state.nodelist.set(n);
224+
try ns.append(id);
225+
}
226+
227+
try ctx.state.nodesearchlist.append(ns);
228+
229+
// output
230+
const Resp = struct {
231+
searchId: []const u8,
232+
resultCount: u32,
233+
};
234+
const resp: Resp = .{
235+
.searchId = ns.name,
236+
.resultCount = @intCast(ln),
130237
};
131238

132239
return result(alloc, input.id, Resp, resp, input.sessionId);

0 commit comments

Comments
 (0)