@@ -14,6 +14,7 @@ const RuntimeMethods = enum {
1414 runIfWaitingForDebugger ,
1515 evaluate ,
1616 addBinding ,
17+ callFunctionOn ,
1718};
1819
1920pub fn runtime (
@@ -30,6 +31,7 @@ pub fn runtime(
3031 .runIfWaitingForDebugger = > runIfWaitingForDebugger (alloc , id , scanner , ctx ),
3132 .evaluate = > evaluate (alloc , id , scanner , ctx ),
3233 .addBinding = > addBinding (alloc , id , scanner , ctx ),
34+ .callFunctionOn = > callFunctionOn (alloc , id , scanner , ctx ),
3335 };
3436}
3537
@@ -194,6 +196,80 @@ fn addBinding(
194196 return result (alloc , id , null , null , msg .sessionID );
195197}
196198
199+ fn callFunctionOn (
200+ alloc : std.mem.Allocator ,
201+ _id : ? u16 ,
202+ scanner : * std.json.Scanner ,
203+ ctx : * Ctx ,
204+ ) ! []const u8 {
205+
206+ // input
207+ const Params = struct {
208+ functionDeclaration : []const u8 ,
209+ objectId : ? []const u8 = null ,
210+ executionContextId : ? u8 = null ,
211+ arguments : ? []struct {
212+ value : ? []const u8 = null ,
213+ } = null ,
214+ returnByValue : ? bool = null ,
215+ awaitPromise : ? bool = null ,
216+ userGesture : ? bool = null ,
217+ };
218+ const msg = try getMsg (alloc , Params , scanner );
219+ const id = _id orelse msg .id .? ;
220+ const params = msg .params .? ;
221+ std .debug .assert (params .objectId != null or params .executionContextId != null );
222+ if (params .executionContextId ) | contextID | {
223+ std .debug .assert (contextID == ctx .state .executionContextId );
224+ }
225+ const name = "callFunctionOn" ;
226+
227+ // save script in file at debug mode
228+ std .log .debug ("{s} script id {d}, length: {d}" , .{ name , id , params .functionDeclaration .len });
229+ if (std .log .defaultLogEnabled (.debug )) {
230+ try cdp .dumpFile (alloc , id , params .functionDeclaration );
231+ }
232+
233+ // parse function
234+ if (! std .mem .startsWith (u8 , params .functionDeclaration , "function " )) {
235+ return error .CDPRuntimeCallFunctionOnNotFunction ;
236+ }
237+ const pos = std .mem .indexOfScalar (u8 , params .functionDeclaration , '(' );
238+ if (pos == null ) return error .CDPRuntimeCallFunctionOnWrongFunction ;
239+ var function = params .functionDeclaration [9.. pos .? ];
240+ function = try std .fmt .allocPrint (alloc , "{s}(" , .{function });
241+ defer alloc .free (function );
242+ if (params .arguments ) | args | {
243+ for (args , 0.. ) | arg , i | {
244+ if (i > 0 ) {
245+ function = try std .fmt .allocPrint (alloc , "{s}, " , .{function });
246+ }
247+ if (arg .value ) | value | {
248+ function = try std .fmt .allocPrint (alloc , "{s}\" {s}\" " , .{ function , value });
249+ } else {
250+ function = try std .fmt .allocPrint (alloc , "{s}undefined" , .{function });
251+ }
252+ }
253+ }
254+ function = try std .fmt .allocPrint (alloc , "{s});" , .{function });
255+ std .log .debug ("{s} id {d}, function parsed: {s}" , .{ name , id , function });
256+
257+ const session = ctx .browser .currentSession ();
258+ // TODO: should we use the page's allocator instead of the session's allocator?
259+ // the following code does not work:
260+ // const page_alloc = ctx.browser.currentSession().page.?.arena.allocator();
261+
262+ // first evaluate the function declaration
263+ const decl = try runtimeEvaluate (session .alloc , id , session .env , params .functionDeclaration , name );
264+ defer decl .deinit (session .alloc );
265+
266+ // then call the function on the arguments
267+ const res = try runtimeEvaluate (session .alloc , id , session .env , function , name );
268+ defer res .deinit (session .alloc );
269+
270+ return result (alloc , id , null , "{\" type\" :\" undefined\" }" , msg .sessionID );
271+ }
272+
197273// caller is the owner of JSResult returned
198274fn runtimeEvaluate (
199275 alloc : std.mem.Allocator ,
0 commit comments