@@ -24,6 +24,7 @@ const cdp = @import("cdp.zig");
2424const result = cdp .result ;
2525const IncomingMessage = @import ("msg.zig" ).IncomingMessage ;
2626const Input = @import ("msg.zig" ).Input ;
27+ const css = @import ("../dom/css.zig" );
2728
2829const parser = @import ("netsurf" );
2930
@@ -32,6 +33,7 @@ const log = std.log.scoped(.cdp);
3233const Methods = enum {
3334 enable ,
3435 getDocument ,
36+ performSearch ,
3537};
3638
3739pub 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+
6598const NodeId = u32 ;
6699
67100const 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