cdp: implement DOM.performSearch

This commit is contained in:
Pierre Tachoire
2025-02-06 17:18:23 +01:00
parent 39b3786776
commit 4e4a8f1bab

View File

@@ -24,6 +24,7 @@ const cdp = @import("cdp.zig");
const result = cdp.result; const result = cdp.result;
const IncomingMessage = @import("msg.zig").IncomingMessage; const IncomingMessage = @import("msg.zig").IncomingMessage;
const Input = @import("msg.zig").Input; const Input = @import("msg.zig").Input;
const css = @import("../dom/css.zig");
const parser = @import("netsurf"); const parser = @import("netsurf");
@@ -32,6 +33,7 @@ const log = std.log.scoped(.cdp);
const Methods = enum { const Methods = enum {
enable, enable,
getDocument, getDocument,
performSearch,
}; };
pub fn dom( pub fn dom(
@@ -46,6 +48,7 @@ pub fn dom(
return switch (method) { return switch (method) {
.enable => enable(alloc, msg, ctx), .enable => enable(alloc, msg, ctx),
.getDocument => getDocument(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); 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 NodeId = u32;
const Node = struct { const Node = struct {
@@ -80,13 +113,13 @@ const Node = struct {
compatibilityMode: []const u8 = "NoQuirksMode", compatibilityMode: []const u8 = "NoQuirksMode",
isScrollable: bool = false, isScrollable: bool = false,
fn init(n: *parser.Node) !Node { fn init(n: *parser.Node, id: NodeId) !Node {
const children = try parser.nodeGetChildNodes(n); const children = try parser.nodeGetChildNodes(n);
const ln = try parser.nodeListLength(children); const ln = try parser.nodeListLength(children);
return .{ return .{
.nodeId = 1, .nodeId = id,
.backendNodeId = 1, .backendNodeId = id,
.nodeType = @intFromEnum(try parser.nodeType(n)), .nodeType = @intFromEnum(try parser.nodeType(n)),
.nodeName = try parser.nodeName(n), .nodeName = try parser.nodeName(n),
.localName = try parser.nodeLocalName(n), .localName = try parser.nodeLocalName(n),
@@ -117,16 +150,90 @@ fn getDocument(
if (page.doc == null) return error.NoDocument; if (page.doc == null) return error.NoDocument;
const root = try parser.documentGetDocumentElement(page.doc.?) orelse { const node = parser.documentToNode(page.doc.?);
return error.NoRoot; const id = try ctx.state.nodelist.set(node);
};
// output // output
const Resp = struct { const Resp = struct {
root: Node, root: Node,
}; };
const resp: Resp = .{ 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); return result(alloc, input.id, Resp, resp, input.sessionId);