@@ -24,11 +24,18 @@ 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" );
28+
29+ const parser = @import ("netsurf" );
2730
2831const log = std .log .scoped (.cdp );
2932
3033const Methods = enum {
3134 enable ,
35+ getDocument ,
36+ performSearch ,
37+ getSearchResults ,
38+ discardSearchResults ,
3239};
3340
3441pub fn dom (
@@ -42,6 +49,10 @@ pub fn dom(
4249
4350 return switch (method ) {
4451 .enable = > enable (alloc , msg , ctx ),
52+ .getDocument = > getDocument (alloc , msg , ctx ),
53+ .performSearch = > performSearch (alloc , msg , ctx ),
54+ .getSearchResults = > getSearchResults (alloc , msg , ctx ),
55+ .discardSearchResults = > discardSearchResults (alloc , msg , ctx ),
4556 };
4657}
4758
@@ -57,3 +68,275 @@ fn enable(
5768
5869 return result (alloc , input .id , null , null , input .sessionId );
5970}
71+
72+ // NodeList references tree nodes with an array id.
73+ pub const NodeList = struct {
74+ coll : List ,
75+
76+ const List = std .ArrayList (* parser .Node );
77+
78+ pub fn init (alloc : std.mem.Allocator ) NodeList {
79+ return .{
80+ .coll = List .init (alloc ),
81+ };
82+ }
83+
84+ pub fn deinit (self : * NodeList ) void {
85+ self .coll .deinit ();
86+ }
87+
88+ pub fn reset (self : * NodeList ) void {
89+ self .coll .clearAndFree ();
90+ }
91+
92+ pub fn set (self : * NodeList , node : * parser.Node ) ! NodeId {
93+ for (self .coll .items , 0.. ) | n , i | {
94+ if (n == node ) return @intCast (i );
95+ }
96+
97+ try self .coll .append (node );
98+ return @intCast (self .coll .items .len );
99+ }
100+ };
101+
102+ const NodeId = u32 ;
103+
104+ const Node = struct {
105+ nodeId : NodeId ,
106+ parentId : ? NodeId = null ,
107+ backendNodeId : NodeId ,
108+ nodeType : u32 ,
109+ nodeName : []const u8 = "" ,
110+ localName : []const u8 = "" ,
111+ nodeValue : []const u8 = "" ,
112+ childNodeCount : ? u32 = null ,
113+ children : ? []const Node = null ,
114+ documentURL : ? []const u8 = null ,
115+ baseURL : ? []const u8 = null ,
116+ xmlVersion : []const u8 = "" ,
117+ compatibilityMode : []const u8 = "NoQuirksMode" ,
118+ isScrollable : bool = false ,
119+
120+ fn init (n : * parser.Node , nlist : * NodeList ) ! Node {
121+ const id = try nlist .set (n );
122+ return .{
123+ .nodeId = id ,
124+ .backendNodeId = id ,
125+ .nodeType = @intFromEnum (try parser .nodeType (n )),
126+ .nodeName = try parser .nodeName (n ),
127+ .localName = try parser .nodeLocalName (n ),
128+ .nodeValue = try parser .nodeValue (n ) orelse "" ,
129+ };
130+ }
131+
132+ fn initChildren (
133+ self : * Node ,
134+ alloc : std.mem.Allocator ,
135+ n : * parser.Node ,
136+ nlist : * NodeList ,
137+ ) ! std. ArrayList (Node ) {
138+ const children = try parser .nodeGetChildNodes (n );
139+ const ln = try parser .nodeListLength (children );
140+ self .childNodeCount = ln ;
141+
142+ var list = try std .ArrayList (Node ).initCapacity (alloc , ln );
143+
144+ var i : u32 = 0 ;
145+ while (i < ln ) {
146+ defer i += 1 ;
147+ const child = try parser .nodeListItem (children , i ) orelse continue ;
148+ try list .append (try Node .init (child , nlist ));
149+ }
150+
151+ self .children = list .items ;
152+
153+ return list ;
154+ }
155+ };
156+
157+ // https://chromedevtools.github.io/devtools-protocol/tot/DOM/#method-getDocument
158+ fn getDocument (
159+ alloc : std.mem.Allocator ,
160+ msg : * IncomingMessage ,
161+ ctx : * Ctx ,
162+ ) ! []const u8 {
163+ // input
164+ const Params = struct {
165+ depth : ? u32 = null ,
166+ pierce : ? bool = null ,
167+ };
168+ const input = try Input (Params ).get (alloc , msg );
169+ defer input .deinit ();
170+ std .debug .assert (input .sessionId != null );
171+ log .debug ("Req > id {d}, method {s}" , .{ input .id , "DOM.getDocument" });
172+
173+ // retrieve the root node
174+ const page = ctx .browser .currentPage () orelse return error .NoPage ;
175+
176+ if (page .doc == null ) return error .NoDocument ;
177+
178+ const node = parser .documentToNode (page .doc .? );
179+ var n = try Node .init (node , & ctx .state .nodelist );
180+ var list = try n .initChildren (alloc , node , & ctx .state .nodelist );
181+ defer list .deinit ();
182+
183+ // output
184+ const Resp = struct {
185+ root : Node ,
186+ };
187+ const resp : Resp = .{
188+ .root = n ,
189+ };
190+
191+ const res = try result (alloc , input .id , Resp , resp , input .sessionId );
192+ try ctx .send (res );
193+
194+ return "" ;
195+ }
196+
197+ pub const NodeSearch = struct {
198+ coll : List ,
199+ name : []u8 ,
200+ alloc : std.mem.Allocator ,
201+
202+ var count : u8 = 0 ;
203+
204+ const List = std .ArrayListUnmanaged (NodeId );
205+
206+ pub fn initCapacity (alloc : std.mem.Allocator , ln : usize ) ! NodeSearch {
207+ count += 1 ;
208+
209+ return .{
210+ .alloc = alloc ,
211+ .coll = try List .initCapacity (alloc , ln ),
212+ .name = try std .fmt .allocPrint (alloc , "{d}" , .{count }),
213+ };
214+ }
215+
216+ pub fn deinit (self : * NodeSearch ) void {
217+ self .coll .deinit (self .alloc );
218+ self .alloc .free (self .name );
219+ }
220+
221+ pub fn append (self : * NodeSearch , id : NodeId ) ! void {
222+ try self .coll .append (self .alloc , id );
223+ }
224+ };
225+ pub const NodeSearchList = std .ArrayList (NodeSearch );
226+
227+ // https://chromedevtools.github.io/devtools-protocol/tot/DOM/#method-performSearch
228+ fn performSearch (
229+ alloc : std.mem.Allocator ,
230+ msg : * IncomingMessage ,
231+ ctx : * Ctx ,
232+ ) ! []const u8 {
233+ // input
234+ const Params = struct {
235+ query : []const u8 ,
236+ includeUserAgentShadowDOM : ? bool = null ,
237+ };
238+ const input = try Input (Params ).get (alloc , msg );
239+ defer input .deinit ();
240+ std .debug .assert (input .sessionId != null );
241+ log .debug ("Req > id {d}, method {s}" , .{ input .id , "DOM.performSearch" });
242+
243+ // retrieve the root node
244+ const page = ctx .browser .currentPage () orelse return error .NoPage ;
245+
246+ if (page .doc == null ) return error .NoDocument ;
247+
248+ const list = try css .querySelectorAll (alloc , parser .documentToNode (page .doc .? ), input .params .query );
249+ const ln = list .nodes .items .len ;
250+ var ns = try NodeSearch .initCapacity (alloc , ln );
251+
252+ for (list .nodes .items ) | n | {
253+ const id = try ctx .state .nodelist .set (n );
254+ try ns .append (id );
255+ }
256+
257+ try ctx .state .nodesearchlist .append (ns );
258+
259+ // output
260+ const Resp = struct {
261+ searchId : []const u8 ,
262+ resultCount : u32 ,
263+ };
264+ const resp : Resp = .{
265+ .searchId = ns .name ,
266+ .resultCount = @intCast (ln ),
267+ };
268+
269+ return result (alloc , input .id , Resp , resp , input .sessionId );
270+ }
271+
272+ // https://chromedevtools.github.io/devtools-protocol/tot/DOM/#method-discardSearchResults
273+ fn discardSearchResults (
274+ alloc : std.mem.Allocator ,
275+ msg : * IncomingMessage ,
276+ ctx : * Ctx ,
277+ ) ! []const u8 {
278+ // input
279+ const Params = struct {
280+ searchId : []const u8 ,
281+ };
282+ const input = try Input (Params ).get (alloc , msg );
283+ defer input .deinit ();
284+ std .debug .assert (input .sessionId != null );
285+ log .debug ("Req > id {d}, method {s}" , .{ input .id , "DOM.discardSearchResults" });
286+
287+ // retrieve the search from context
288+ for (ctx .state .nodesearchlist .items , 0.. ) | * s , i | {
289+ if (! std .mem .eql (u8 , s .name , input .params .searchId )) continue ;
290+
291+ s .deinit ();
292+ _ = ctx .state .nodesearchlist .swapRemove (i );
293+ break ;
294+ }
295+
296+ return result (alloc , input .id , null , null , input .sessionId );
297+ }
298+
299+ // https://chromedevtools.github.io/devtools-protocol/tot/DOM/#method-getSearchResults
300+ fn getSearchResults (
301+ alloc : std.mem.Allocator ,
302+ msg : * IncomingMessage ,
303+ ctx : * Ctx ,
304+ ) ! []const u8 {
305+ // input
306+ const Params = struct {
307+ searchId : []const u8 ,
308+ fromIndex : u32 ,
309+ toIndex : u32 ,
310+ };
311+ const input = try Input (Params ).get (alloc , msg );
312+ defer input .deinit ();
313+ std .debug .assert (input .sessionId != null );
314+ log .debug ("Req > id {d}, method {s}" , .{ input .id , "DOM.getSearchResults" });
315+
316+ if (input .params .fromIndex >= input .params .toIndex ) return error .BadIndices ;
317+
318+ // retrieve the search from context
319+ var ns : ? * const NodeSearch = undefined ;
320+ for (ctx .state .nodesearchlist .items ) | s | {
321+ if (! std .mem .eql (u8 , s .name , input .params .searchId )) continue ;
322+
323+ ns = & s ;
324+ break ;
325+ }
326+
327+ if (ns == null ) return error .searchResultNotFound ;
328+ const items = ns .? .coll .items ;
329+
330+ if (input .params .fromIndex >= items .len ) return error .BadFromIndex ;
331+ if (input .params .toIndex > items .len ) return error .BadToIndex ;
332+
333+ // output
334+ const Resp = struct {
335+ nodeIds : []NodeId ,
336+ };
337+ const resp : Resp = .{
338+ .nodeIds = ns .? .coll .items [input .params .fromIndex .. input .params .toIndex ],
339+ };
340+
341+ return result (alloc , input .id , Resp , resp , input .sessionId );
342+ }
0 commit comments