From 82c37fc71bf0d958e8ed168743d8dd699552ac34 Mon Sep 17 00:00:00 2001 From: Pierre Tachoire Date: Tue, 29 Oct 2024 09:49:36 +0100 Subject: [PATCH 1/2] cdp: refacto message JSON read --- src/cdp/browser.zig | 53 +++++----- src/cdp/cdp.zig | 154 +++-------------------------- src/cdp/emulation.zig | 51 +++++----- src/cdp/fetch.zig | 16 ++- src/cdp/log.zig | 16 ++- src/cdp/msg.zig | 214 ++++++++++++++++++++++++++++++++++++++++ src/cdp/network.zig | 29 +++--- src/cdp/page.zig | 113 +++++++++------------ src/cdp/performance.zig | 17 ++-- src/cdp/runtime.zig | 51 ++++------ src/cdp/target.zig | 126 ++++++++++------------- 11 files changed, 431 insertions(+), 409 deletions(-) create mode 100644 src/cdp/msg.zig diff --git a/src/cdp/browser.zig b/src/cdp/browser.zig index 4bfe17a2..9a19c7a2 100644 --- a/src/cdp/browser.zig +++ b/src/cdp/browser.zig @@ -22,7 +22,7 @@ const server = @import("../server.zig"); const Ctx = server.Ctx; const cdp = @import("cdp.zig"); const result = cdp.result; -const getMsg = cdp.getMsg; +const IncomingMessage = @import("msg.zig").IncomingMessage; const log = std.log.scoped(.cdp); @@ -35,18 +35,17 @@ const Methods = enum { pub fn browser( alloc: std.mem.Allocator, - id: ?u16, + msg: *IncomingMessage, action: []const u8, - scanner: *std.json.Scanner, ctx: *Ctx, ) ![]const u8 { const method = std.meta.stringToEnum(Methods, action) orelse return error.UnknownMethod; return switch (method) { - .getVersion => getVersion(alloc, id, scanner, ctx), - .setDownloadBehavior => setDownloadBehavior(alloc, id, scanner, ctx), - .getWindowForTarget => getWindowForTarget(alloc, id, scanner, ctx), - .setWindowBounds => setWindowBounds(alloc, id, scanner, ctx), + .getVersion => getVersion(alloc, msg, ctx), + .setDownloadBehavior => setDownloadBehavior(alloc, msg, ctx), + .getWindowForTarget => getWindowForTarget(alloc, msg, ctx), + .setWindowBounds => setWindowBounds(alloc, msg, ctx), }; } @@ -59,14 +58,12 @@ const JsVersion = "12.4.254.8"; fn getVersion( alloc: std.mem.Allocator, - _id: ?u16, - scanner: *std.json.Scanner, + msg: *IncomingMessage, _: *Ctx, ) ![]const u8 { - // input - const msg = try getMsg(alloc, _id, void, scanner); - log.debug("Req > id {d}, method {s}", .{ msg.id, "browser.getVersion" }); + const input = try msg.getInput(alloc, void); + log.debug("Req > id {d}, method {s}", .{ input.id, "browser.getVersion" }); // ouput const Res = struct { @@ -76,17 +73,15 @@ fn getVersion( userAgent: []const u8 = UserAgent, jsVersion: []const u8 = JsVersion, }; - return result(alloc, msg.id, Res, .{}, null); + return result(alloc, input.id, Res, .{}, null); } // TODO: noop method fn setDownloadBehavior( alloc: std.mem.Allocator, - _id: ?u16, - scanner: *std.json.Scanner, + msg: *IncomingMessage, _: *Ctx, ) ![]const u8 { - // input const Params = struct { behavior: []const u8, @@ -94,11 +89,11 @@ fn setDownloadBehavior( downloadPath: ?[]const u8 = null, eventsEnabled: ?bool = null, }; - const msg = try getMsg(alloc, _id, Params, scanner); - log.debug("REQ > id {d}, method {s}", .{ msg.id, "browser.setDownloadBehavior" }); + const input = try msg.getInput(alloc, Params); + log.debug("REQ > id {d}, method {s}", .{ input.id, "browser.setDownloadBehavior" }); // output - return result(alloc, msg.id, null, null, null); + return result(alloc, input.id, null, null, null); } // TODO: hard coded ID @@ -106,8 +101,7 @@ const DevToolsWindowID = 1923710101; fn getWindowForTarget( alloc: std.mem.Allocator, - _id: ?u16, - scanner: *std.json.Scanner, + msg: *IncomingMessage, _: *Ctx, ) ![]const u8 { @@ -115,9 +109,9 @@ fn getWindowForTarget( const Params = struct { targetId: ?[]const u8 = null, }; - const msg = try cdp.getMsg(alloc, _id, ?Params, scanner); - std.debug.assert(msg.sessionID != null); - log.debug("Req > id {d}, method {s}", .{ msg.id, "browser.getWindowForTarget" }); + const input = try msg.getInput(alloc, Params); + std.debug.assert(input.sessionId != null); + log.debug("Req > id {d}, method {s}", .{ input.id, "browser.getWindowForTarget" }); // output const Resp = struct { @@ -130,21 +124,20 @@ fn getWindowForTarget( windowState: []const u8 = "normal", } = .{}, }; - return result(alloc, msg.id, Resp, Resp{}, msg.sessionID.?); + return result(alloc, input.id, Resp, Resp{}, input.sessionId); } // TODO: noop method fn setWindowBounds( alloc: std.mem.Allocator, - _id: ?u16, - scanner: *std.json.Scanner, + msg: *IncomingMessage, _: *Ctx, ) ![]const u8 { + const input = try msg.getInput(alloc, void); // input - const msg = try cdp.getMsg(alloc, _id, void, scanner); - log.debug("Req > id {d}, method {s}", .{ msg.id, "browser.setWindowBounds" }); + log.debug("Req > id {d}, method {s}", .{ input.id, "browser.setWindowBounds" }); // output - return result(alloc, msg.id, null, null, msg.sessionID); + return result(alloc, input.id, null, null, input.sessionId); } diff --git a/src/cdp/cdp.zig b/src/cdp/cdp.zig index f555da73..14c7e3b0 100644 --- a/src/cdp/cdp.zig +++ b/src/cdp/cdp.zig @@ -30,6 +30,7 @@ const network = @import("network.zig").network; const emulation = @import("emulation.zig").emulation; const fetch = @import("fetch.zig").fetch; const performance = @import("performance.zig").performance; +const IncomingMessage = @import("msg.zig").IncomingMessage; const log_cdp = std.log.scoped(.cdp); @@ -70,51 +71,29 @@ pub fn do( ctx: *Ctx, ) ![]const u8 { - // JSON scanner - var scanner = std.json.Scanner.initCompleteInput(alloc, s); - defer scanner.deinit(); + // incoming message parser + var msg = IncomingMessage.init(alloc, s); + defer msg.deinit(); - std.debug.assert(try scanner.next() == .object_begin); - - // handle 2 possible orders: - // - id, method <...> - // - method, id <...> - var method_key = try nextString(&scanner); - var method_token: std.json.Token = undefined; - var id: ?u16 = null; - // check swap order - if (std.mem.eql(u8, method_key, "id")) { - id = try getId(&scanner, method_key); - method_key = try nextString(&scanner); - method_token = try scanner.next(); - } else { - method_token = try scanner.next(); - } - try checkKey(method_key, "method"); - - // retrieve method - if (method_token != .string) { - return error.WrongTokenType; - } - const method_name = method_token.string; + const method = try msg.getMethod(); // retrieve domain from method - var iter = std.mem.splitScalar(u8, method_name, '.'); + var iter = std.mem.splitScalar(u8, method, '.'); const domain = std.meta.stringToEnum(Domains, iter.first()) orelse return error.UnknonwDomain; // select corresponding domain const action = iter.next() orelse return error.BadMethod; return switch (domain) { - .Browser => browser(alloc, id, action, &scanner, ctx), - .Target => target(alloc, id, action, &scanner, ctx), - .Page => page(alloc, id, action, &scanner, ctx), - .Log => log(alloc, id, action, &scanner, ctx), - .Runtime => runtime(alloc, id, action, &scanner, s, ctx), - .Network => network(alloc, id, action, &scanner, ctx), - .Emulation => emulation(alloc, id, action, &scanner, ctx), - .Fetch => fetch(alloc, id, action, &scanner, ctx), - .Performance => performance(alloc, id, action, &scanner, ctx), + .Browser => browser(alloc, &msg, action, ctx), + .Target => target(alloc, &msg, action, ctx), + .Page => page(alloc, &msg, action, ctx), + .Log => log(alloc, &msg, action, ctx), + .Runtime => runtime(alloc, &msg, action, ctx), + .Network => network(alloc, &msg, action, ctx), + .Emulation => emulation(alloc, &msg, action, ctx), + .Fetch => fetch(alloc, &msg, action, ctx), + .Performance => performance(alloc, &msg, action, ctx), }; } @@ -133,14 +112,6 @@ pub const State = struct { // Utils // ----- -fn nextString(scanner: *std.json.Scanner) ![]const u8 { - const token = try scanner.next(); - if (token != .string) { - return error.WrongTokenType; - } - return token.string; -} - pub fn dumpFile( alloc: std.mem.Allocator, id: u16, @@ -158,10 +129,6 @@ pub fn dumpFile( defer alloc.free(p); } -fn checkKey(key: []const u8, token: []const u8) !void { - if (!std.mem.eql(u8, key, token)) return error.WrongToken; -} - // caller owns the slice returned pub fn stringify(alloc: std.mem.Allocator, res: anytype) ![]const u8 { var out = std.ArrayList(u8).init(alloc); @@ -229,97 +196,6 @@ pub fn sendEvent( try server.sendAsync(ctx, event_msg); } -fn getParams( - alloc: std.mem.Allocator, - comptime T: type, - scanner: *std.json.Scanner, - key: []const u8, -) !?T { - - // check key is "params" - if (!std.mem.eql(u8, "params", key)) return null; - - // skip "params" if not requested - if (T == void) { - var finished: usize = 0; - while (true) { - switch (try scanner.next()) { - .object_begin => finished += 1, - .object_end => finished -= 1, - else => continue, - } - if (finished == 0) break; - } - return void{}; - } - - // parse "params" - const options = std.json.ParseOptions{ - .max_value_len = scanner.input.len, - .allocate = .alloc_if_needed, - }; - return try std.json.innerParse(T, alloc, scanner, options); -} - -fn getId(scanner: *std.json.Scanner, key: []const u8) !?u16 { - - // check key is "id" - if (!std.mem.eql(u8, "id", key)) return null; - - // parse "id" - return try std.fmt.parseUnsigned(u16, (try scanner.next()).number, 10); -} - -fn getSessionId(scanner: *std.json.Scanner, key: []const u8) !?[]const u8 { - - // check key is "sessionId" - if (!std.mem.eql(u8, "sessionId", key)) return null; - - // parse "sessionId" - return try nextString(scanner); -} - -pub fn getMsg( - alloc: std.mem.Allocator, - _id: ?u16, - comptime params_T: type, - scanner: *std.json.Scanner, -) !struct { id: u16, params: ?params_T, sessionID: ?[]const u8 } { - var id_msg: ?u16 = null; - var params: ?params_T = null; - var sessionID: ?[]const u8 = null; - - var t: std.json.Token = undefined; - - while (true) { - t = try scanner.next(); - if (t == .object_end) break; - if (t != .string) { - return error.WrongTokenType; - } - if (_id == null and id_msg == null) { - id_msg = try getId(scanner, t.string); - if (id_msg != null) continue; - } - if (params == null) { - params = try getParams(alloc, params_T, scanner, t.string); - if (params != null) continue; - } - if (sessionID == null) { - sessionID = try getSessionId(scanner, t.string); - } - } - - // end - t = try scanner.next(); - if (t != .end_of_document) return error.CDPMsgEnd; - - // check id - if (_id == null and id_msg == null) return error.RequestWithoutID; - - return .{ .id = _id orelse id_msg.?, .params = params, .sessionID = sessionID }; -} - // Common // ------ diff --git a/src/cdp/emulation.zig b/src/cdp/emulation.zig index 4bb32bc3..c51e20f3 100644 --- a/src/cdp/emulation.zig +++ b/src/cdp/emulation.zig @@ -22,8 +22,8 @@ const server = @import("../server.zig"); const Ctx = server.Ctx; const cdp = @import("cdp.zig"); const result = cdp.result; -const getMsg = cdp.getMsg; const stringify = cdp.stringify; +const IncomingMessage = @import("msg.zig").IncomingMessage; const log = std.log.scoped(.cdp); @@ -36,18 +36,17 @@ const Methods = enum { pub fn emulation( alloc: std.mem.Allocator, - id: ?u16, + msg: *IncomingMessage, action: []const u8, - scanner: *std.json.Scanner, ctx: *Ctx, ) ![]const u8 { const method = std.meta.stringToEnum(Methods, action) orelse return error.UnknownMethod; return switch (method) { - .setEmulatedMedia => setEmulatedMedia(alloc, id, scanner, ctx), - .setFocusEmulationEnabled => setFocusEmulationEnabled(alloc, id, scanner, ctx), - .setDeviceMetricsOverride => setDeviceMetricsOverride(alloc, id, scanner, ctx), - .setTouchEmulationEnabled => setTouchEmulationEnabled(alloc, id, scanner, ctx), + .setEmulatedMedia => setEmulatedMedia(alloc, msg, ctx), + .setFocusEmulationEnabled => setFocusEmulationEnabled(alloc, msg, ctx), + .setDeviceMetricsOverride => setDeviceMetricsOverride(alloc, msg, ctx), + .setTouchEmulationEnabled => setTouchEmulationEnabled(alloc, msg, ctx), }; } @@ -59,8 +58,7 @@ const MediaFeature = struct { // TODO: noop method fn setEmulatedMedia( alloc: std.mem.Allocator, - _id: ?u16, - scanner: *std.json.Scanner, + msg: *IncomingMessage, _: *Ctx, ) ![]const u8 { @@ -69,57 +67,52 @@ fn setEmulatedMedia( media: ?[]const u8 = null, features: ?[]MediaFeature = null, }; - const msg = try getMsg(alloc, _id, Params, scanner); - log.debug("Req > id {d}, method {s}", .{ msg.id, "emulation.setEmulatedMedia" }); + const input = try msg.getInput(alloc, Params); + log.debug("Req > id {d}, method {s}", .{ input.id, "emulation.setEmulatedMedia" }); // output - return result(alloc, msg.id, null, null, msg.sessionID); + return result(alloc, input.id, null, null, input.sessionId); } // TODO: noop method fn setFocusEmulationEnabled( alloc: std.mem.Allocator, - _id: ?u16, - scanner: *std.json.Scanner, + msg: *IncomingMessage, _: *Ctx, ) ![]const u8 { - // input const Params = struct { enabled: bool, }; - const msg = try getMsg(alloc, _id, Params, scanner); - log.debug("Req > id {d}, method {s}", .{ msg.id, "emulation.setFocusEmulationEnabled" }); + const input = try msg.getInput(alloc, Params); + log.debug("Req > id {d}, method {s}", .{ input.id, "emulation.setFocusEmulationEnabled" }); // output - return result(alloc, msg.id, null, null, msg.sessionID); + return result(alloc, input.id, null, null, input.sessionId); } // TODO: noop method fn setDeviceMetricsOverride( alloc: std.mem.Allocator, - _id: ?u16, - scanner: *std.json.Scanner, + msg: *IncomingMessage, _: *Ctx, ) ![]const u8 { - // input - const msg = try cdp.getMsg(alloc, _id, void, scanner); - log.debug("Req > id {d}, method {s}", .{ msg.id, "emulation.setDeviceMetricsOverride" }); + const input = try msg.getInput(alloc, void); + log.debug("Req > id {d}, method {s}", .{ input.id, "emulation.setDeviceMetricsOverride" }); // output - return result(alloc, msg.id, null, null, msg.sessionID); + return result(alloc, input.id, null, null, input.sessionId); } // TODO: noop method fn setTouchEmulationEnabled( alloc: std.mem.Allocator, - _id: ?u16, - scanner: *std.json.Scanner, + msg: *IncomingMessage, _: *Ctx, ) ![]const u8 { - const msg = try cdp.getMsg(alloc, _id, void, scanner); - log.debug("Req > id {d}, method {s}", .{ msg.id, "emulation.setTouchEmulationEnabled" }); + const input = try msg.getInput(alloc, void); + log.debug("Req > id {d}, method {s}", .{ input.id, "emulation.setTouchEmulationEnabled" }); - return result(alloc, msg.id, null, null, msg.sessionID); + return result(alloc, input.id, null, null, input.sessionId); } diff --git a/src/cdp/fetch.zig b/src/cdp/fetch.zig index 2bdeecab..5e54a13a 100644 --- a/src/cdp/fetch.zig +++ b/src/cdp/fetch.zig @@ -22,7 +22,7 @@ const server = @import("../server.zig"); const Ctx = server.Ctx; const cdp = @import("cdp.zig"); const result = cdp.result; -const getMsg = cdp.getMsg; +const IncomingMessage = @import("msg.zig").IncomingMessage; const log = std.log.scoped(.cdp); @@ -32,28 +32,26 @@ const Methods = enum { pub fn fetch( alloc: std.mem.Allocator, - id: ?u16, + msg: *IncomingMessage, action: []const u8, - scanner: *std.json.Scanner, ctx: *Ctx, ) ![]const u8 { const method = std.meta.stringToEnum(Methods, action) orelse return error.UnknownMethod; return switch (method) { - .disable => disable(alloc, id, scanner, ctx), + .disable => disable(alloc, msg, ctx), }; } // TODO: noop method fn disable( alloc: std.mem.Allocator, - _id: ?u16, - scanner: *std.json.Scanner, + msg: *IncomingMessage, _: *Ctx, ) ![]const u8 { - const msg = try getMsg(alloc, _id, void, scanner); - log.debug("Req > id {d}, method {s}", .{ msg.id, "fetch.disable" }); + const input = try msg.getInput(alloc, void); + log.debug("Req > id {d}, method {s}", .{ input.id, "fetch.disable" }); - return result(alloc, msg.id, null, null, msg.sessionID); + return result(alloc, input.id, null, null, input.sessionId); } diff --git a/src/cdp/log.zig b/src/cdp/log.zig index d2cf5ccc..7ebcf241 100644 --- a/src/cdp/log.zig +++ b/src/cdp/log.zig @@ -22,7 +22,7 @@ const server = @import("../server.zig"); const Ctx = server.Ctx; const cdp = @import("cdp.zig"); const result = cdp.result; -const getMsg = cdp.getMsg; +const IncomingMessage = @import("msg.zig").IncomingMessage; const stringify = cdp.stringify; const log_cdp = std.log.scoped(.cdp); @@ -33,27 +33,25 @@ const Methods = enum { pub fn log( alloc: std.mem.Allocator, - id: ?u16, + msg: *IncomingMessage, action: []const u8, - scanner: *std.json.Scanner, ctx: *Ctx, ) ![]const u8 { const method = std.meta.stringToEnum(Methods, action) orelse return error.UnknownMethod; return switch (method) { - .enable => enable(alloc, id, scanner, ctx), + .enable => enable(alloc, msg, ctx), }; } fn enable( alloc: std.mem.Allocator, - _id: ?u16, - scanner: *std.json.Scanner, + msg: *IncomingMessage, _: *Ctx, ) ![]const u8 { - const msg = try getMsg(alloc, _id, void, scanner); - log_cdp.debug("Req > id {d}, method {s}", .{ msg.id, "log.enable" }); + const input = try msg.getInput(alloc, void); + log_cdp.debug("Req > id {d}, method {s}", .{ input.id, "log.enable" }); - return result(alloc, msg.id, null, null, msg.sessionID); + return result(alloc, input.id, null, null, input.sessionId); } diff --git a/src/cdp/msg.zig b/src/cdp/msg.zig new file mode 100644 index 00000000..a50cfd4a --- /dev/null +++ b/src/cdp/msg.zig @@ -0,0 +1,214 @@ +// Copyright (C) 2023-2024 Lightpanda (Selecy SAS) +// +// Francis Bouvier +// Pierre Tachoire +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +const std = @import("std"); + +// Parse incoming protocol message in json format. +pub const IncomingMessage = struct { + scanner: std.json.Scanner, + json: []const u8, + + obj_begin: bool = false, + obj_end: bool = false, + + id: ?u16 = null, + scan_sessionId: bool = false, + sessionId: ?[]const u8 = null, + method: ?[]const u8 = null, + params_skip: bool = false, + + pub fn init(alloc: std.mem.Allocator, json: []const u8) IncomingMessage { + return .{ + .json = json, + .scanner = std.json.Scanner.initCompleteInput(alloc, json), + }; + } + + pub fn deinit(self: *IncomingMessage) void { + self.scanner.deinit(); + } + + fn scanUntil(self: *IncomingMessage, key: []const u8) !void { + while (true) { + switch (try self.scanner.next()) { + .end_of_document => return error.EndOfDocument, + .object_begin => { + if (self.obj_begin) return error.InvalidObjectBegin; + self.obj_begin = true; + }, + .object_end => { + if (!self.obj_begin) return error.InvalidObjectEnd; + if (self.obj_end) return error.InvalidObjectEnd; + self.obj_end = true; + }, + .string => |s| { + // is the key what we expects? + if (std.mem.eql(u8, s, key)) return; + + // save other known keys + if (std.mem.eql(u8, s, "id")) try self.scanId(); + if (std.mem.eql(u8, s, "sessionId")) try self.scanSessionId(); + if (std.mem.eql(u8, s, "method")) try self.scanMethod(); + if (std.mem.eql(u8, s, "params")) try self.scanParams(); + + // TODO should we skip unknown key? + }, + else => return error.InvalidToken, + } + } + } + + fn scanId(self: *IncomingMessage) !void { + const t = try self.scanner.next(); + if (t != .number) return error.InvalidId; + self.id = try std.fmt.parseUnsigned(u16, t.number, 10); + } + + fn getId(self: *IncomingMessage) !u16 { + if (self.id != null) return self.id.?; + + try self.scanUntil("id"); + try self.scanId(); + return self.id.?; + } + + fn scanSessionId(self: *IncomingMessage) !void { + switch (try self.scanner.next()) { + // session id can be null. + .null => return, + .string => |s| self.sessionId = s, + else => return error.InvalidSessionId, + } + + self.scan_sessionId = true; + } + + fn getSessionId(self: *IncomingMessage) !?[]const u8 { + if (self.scan_sessionId) return self.sessionId; + + self.scanUntil("sessionId") catch |err| { + if (err != error.EndOfDocument) return err; + // if the document doesn't contains any session id key, we must + // return null value. + self.scan_sessionId = true; + return null; + }; + try self.scanSessionId(); + return self.sessionId; + } + + fn scanMethod(self: *IncomingMessage) !void { + const t = try self.scanner.next(); + if (t != .string) return error.InvalidMethod; + self.method = t.string; + } + + pub fn getMethod(self: *IncomingMessage) ![]const u8 { + if (self.method != null) return self.method.?; + + try self.scanUntil("method"); + try self.scanMethod(); + return self.method.?; + } + + // scanParams skip found parameters b/c if we encounter params *before* + // asking for getParams, we don't know how to parse them. + fn scanParams(self: *IncomingMessage) !void { + const tt = try self.scanner.peekNextTokenType(); + if (tt != .object_begin) return error.InvalidParams; + try self.scanner.skipValue(); + self.params_skip = true; + } + + // getParams restart the JSON parsing + fn getParams(self: *IncomingMessage, alloc: std.mem.Allocator, T: type) !T { + if (T == void) return void{}; + + if (self.params_skip) { + // TODO if the params have been skipped, we have to retart the + // parsing from start. + return error.SkippedParams; + } + + try self.scanUntil("params"); + + // parse "params" + const options = std.json.ParseOptions{ + .max_value_len = self.scanner.input.len, + .allocate = .alloc_if_needed, + }; + return try std.json.innerParse(T, alloc, &self.scanner, options); + } + + pub fn getInput(self: *IncomingMessage, alloc: std.mem.Allocator, T: type) !struct { id: u16, sessionId: ?[]const u8, params: T } { + return .{ + .params = try self.getParams(alloc, T), + .id = try self.getId(), + .sessionId = try self.getSessionId(), + }; + } +}; + +test "read incoming message" { + const inputs = [_][]const u8{ + \\{"id":1,"method":"foo","sessionId":"bar","params":{"bar":"baz"}} + , + \\{"params":{"bar":"baz"},"id":1,"method":"foo","sessionId":"bar"} + , + \\{"sessionId":"bar","params":{"bar":"baz"},"id":1,"method":"foo"} + , + \\{"method":"foo","sessionId":"bar","params":{"bar":"baz"},"id":1} + , + }; + + for (inputs) |input| { + var msg = IncomingMessage.init(std.testing.allocator, input); + defer msg.deinit(); + + try std.testing.expectEqual(1, try msg.getId()); + try std.testing.expectEqualSlices(u8, "foo", try msg.getMethod()); + try std.testing.expectEqualSlices(u8, "bar", (try msg.getSessionId()).?); + + const T = struct { bar: []const u8 }; + const in = msg.getInput(std.testing.allocator, T) catch |err| { + if (err != error.SkippedParams) return err; + // TODO remove this check when params in the beginning is handled. + continue; + }; + try std.testing.expectEqualSlices(u8, "baz", in.params.bar); + } +} + +test "read incoming message with null session id" { + const inputs = [_][]const u8{ + \\{"id":1} + , + \\{"params":{"bar":"baz"},"id":1,"method":"foo"} + , + \\{"sessionId":null,"params":{"bar":"baz"},"id":1,"method":"foo"} + , + }; + + for (inputs) |input| { + var msg = IncomingMessage.init(std.testing.allocator, input); + defer msg.deinit(); + + try std.testing.expect(try msg.getSessionId() == null); + try std.testing.expectEqual(1, try msg.getId()); + } +} diff --git a/src/cdp/network.zig b/src/cdp/network.zig index 15c93df3..7a7ffe79 100644 --- a/src/cdp/network.zig +++ b/src/cdp/network.zig @@ -22,7 +22,7 @@ const server = @import("../server.zig"); const Ctx = server.Ctx; const cdp = @import("cdp.zig"); const result = cdp.result; -const getMsg = cdp.getMsg; +const IncomingMessage = @import("msg.zig").IncomingMessage; const log = std.log.scoped(.cdp); @@ -33,45 +33,40 @@ const Methods = enum { pub fn network( alloc: std.mem.Allocator, - id: ?u16, + msg: *IncomingMessage, action: []const u8, - scanner: *std.json.Scanner, ctx: *Ctx, ) ![]const u8 { const method = std.meta.stringToEnum(Methods, action) orelse return error.UnknownMethod; return switch (method) { - .enable => enable(alloc, id, scanner, ctx), - .setCacheDisabled => setCacheDisabled(alloc, id, scanner, ctx), + .enable => enable(alloc, msg, ctx), + .setCacheDisabled => setCacheDisabled(alloc, msg, ctx), }; } fn enable( alloc: std.mem.Allocator, - _id: ?u16, - scanner: *std.json.Scanner, + msg: *IncomingMessage, _: *Ctx, ) ![]const u8 { - // input - const msg = try getMsg(alloc, _id, void, scanner); - log.debug("Req > id {d}, method {s}", .{ msg.id, "network.enable" }); + const input = try msg.getInput(alloc, void); + log.debug("Req > id {d}, method {s}", .{ input.id, "network.enable" }); - return result(alloc, msg.id, null, null, msg.sessionID); + return result(alloc, input.id, null, null, input.sessionId); } // TODO: noop method fn setCacheDisabled( alloc: std.mem.Allocator, - _id: ?u16, - scanner: *std.json.Scanner, + msg: *IncomingMessage, _: *Ctx, ) ![]const u8 { - // input - const msg = try getMsg(alloc, _id, void, scanner); - log.debug("Req > id {d}, method {s}", .{ msg.id, "network.setCacheDisabled" }); + const input = try msg.getInput(alloc, void); + log.debug("Req > id {d}, method {s}", .{ input.id, "network.setCacheDisabled" }); - return result(alloc, msg.id, null, null, msg.sessionID); + return result(alloc, input.id, null, null, input.sessionId); } diff --git a/src/cdp/page.zig b/src/cdp/page.zig index 8404ea42..00870234 100644 --- a/src/cdp/page.zig +++ b/src/cdp/page.zig @@ -22,9 +22,9 @@ const server = @import("../server.zig"); const Ctx = server.Ctx; const cdp = @import("cdp.zig"); const result = cdp.result; -const getMsg = cdp.getMsg; const stringify = cdp.stringify; const sendEvent = cdp.sendEvent; +const IncomingMessage = @import("msg.zig").IncomingMessage; const log = std.log.scoped(.cdp); @@ -41,35 +41,32 @@ const Methods = enum { pub fn page( alloc: std.mem.Allocator, - id: ?u16, + msg: *IncomingMessage, action: []const u8, - scanner: *std.json.Scanner, ctx: *Ctx, ) ![]const u8 { const method = std.meta.stringToEnum(Methods, action) orelse return error.UnknownMethod; return switch (method) { - .enable => enable(alloc, id, scanner, ctx), - .getFrameTree => getFrameTree(alloc, id, scanner, ctx), - .setLifecycleEventsEnabled => setLifecycleEventsEnabled(alloc, id, scanner, ctx), - .addScriptToEvaluateOnNewDocument => addScriptToEvaluateOnNewDocument(alloc, id, scanner, ctx), - .createIsolatedWorld => createIsolatedWorld(alloc, id, scanner, ctx), - .navigate => navigate(alloc, id, scanner, ctx), + .enable => enable(alloc, msg, ctx), + .getFrameTree => getFrameTree(alloc, msg, ctx), + .setLifecycleEventsEnabled => setLifecycleEventsEnabled(alloc, msg, ctx), + .addScriptToEvaluateOnNewDocument => addScriptToEvaluateOnNewDocument(alloc, msg, ctx), + .createIsolatedWorld => createIsolatedWorld(alloc, msg, ctx), + .navigate => navigate(alloc, msg, ctx), }; } fn enable( alloc: std.mem.Allocator, - _id: ?u16, - scanner: *std.json.Scanner, + msg: *IncomingMessage, _: *Ctx, ) ![]const u8 { - // input - const msg = try getMsg(alloc, _id, void, scanner); - log.debug("Req > id {d}, method {s}", .{ msg.id, "page.enable" }); + const input = try msg.getInput(alloc, void); + log.debug("Req > id {d}, method {s}", .{ input.id, "page.enable" }); - return result(alloc, msg.id, null, null, msg.sessionID); + return result(alloc, input.id, null, null, input.sessionId); } const Frame = struct { @@ -89,14 +86,12 @@ const Frame = struct { fn getFrameTree( alloc: std.mem.Allocator, - _id: ?u16, - scanner: *std.json.Scanner, + msg: *IncomingMessage, ctx: *Ctx, ) ![]const u8 { - // input - const msg = try cdp.getMsg(alloc, _id, void, scanner); - log.debug("Req > id {d}, method {s}", .{ msg.id, "page.getFrameTree" }); + const input = try msg.getInput(alloc, void); + log.debug("Req > id {d}, method {s}", .{ input.id, "page.getFrameTree" }); // output const FrameTree = struct { @@ -135,27 +130,25 @@ fn getFrameTree( }, }, }; - return result(alloc, msg.id, FrameTree, frameTree, msg.sessionID); + return result(alloc, input.id, FrameTree, frameTree, input.sessionId); } fn setLifecycleEventsEnabled( alloc: std.mem.Allocator, - _id: ?u16, - scanner: *std.json.Scanner, + msg: *IncomingMessage, ctx: *Ctx, ) ![]const u8 { - // input const Params = struct { enabled: bool, }; - const msg = try getMsg(alloc, _id, Params, scanner); - log.debug("Req > id {d}, method {s}", .{ msg.id, "page.setLifecycleEventsEnabled" }); + const input = try msg.getInput(alloc, Params); + log.debug("Req > id {d}, method {s}", .{ input.id, "page.setLifecycleEventsEnabled" }); ctx.state.page_life_cycle_events = true; // output - return result(alloc, msg.id, null, null, msg.sessionID); + return result(alloc, input.id, null, null, input.sessionId); } const LifecycleEvent = struct { @@ -168,11 +161,9 @@ const LifecycleEvent = struct { // TODO: hard coded method fn addScriptToEvaluateOnNewDocument( alloc: std.mem.Allocator, - _id: ?u16, - scanner: *std.json.Scanner, + msg: *IncomingMessage, _: *Ctx, ) ![]const u8 { - // input const Params = struct { source: []const u8, @@ -180,8 +171,8 @@ fn addScriptToEvaluateOnNewDocument( includeCommandLineAPI: bool = false, runImmediately: bool = false, }; - const msg = try getMsg(alloc, _id, Params, scanner); - log.debug("Req > id {d}, method {s}", .{ msg.id, "page.addScriptToEvaluateOnNewDocument" }); + const input = try msg.getInput(alloc, Params); + log.debug("Req > id {d}, method {s}", .{ input.id, "page.addScriptToEvaluateOnNewDocument" }); // output const Res = struct { @@ -199,27 +190,24 @@ fn addScriptToEvaluateOnNewDocument( try writer.writeAll(" }"); } }; - return result(alloc, msg.id, Res, Res{}, msg.sessionID); + return result(alloc, input.id, Res, Res{}, input.sessionId); } // TODO: hard coded method fn createIsolatedWorld( alloc: std.mem.Allocator, - _id: ?u16, - scanner: *std.json.Scanner, + msg: *IncomingMessage, ctx: *Ctx, ) ![]const u8 { - // input const Params = struct { frameId: []const u8, worldName: []const u8, grantUniveralAccess: bool, }; - const msg = try getMsg(alloc, _id, Params, scanner); - std.debug.assert(msg.sessionID != null); - log.debug("Req > id {d}, method {s}", .{ msg.id, "page.createIsolatedWorld" }); - const params = msg.params.?; + const input = try msg.getInput(alloc, Params); + std.debug.assert(input.sessionId != null); + log.debug("Req > id {d}, method {s}", .{ input.id, "page.createIsolatedWorld" }); // noop executionContextCreated event try Runtime.executionContextCreated( @@ -227,15 +215,15 @@ fn createIsolatedWorld( ctx, 0, "", - params.worldName, + input.params.worldName, // TODO: hard coded ID "7102379147004877974.3265385113993241162", .{ .isDefault = false, .type = "isolated", - .frameId = params.frameId, + .frameId = input.params.frameId, }, - msg.sessionID, + input.sessionId, ); // output @@ -243,16 +231,14 @@ fn createIsolatedWorld( executionContextId: u8 = 0, }; - return result(alloc, msg.id, Resp, .{}, msg.sessionID); + return result(alloc, input.id, Resp, .{}, input.sessionId); } fn navigate( alloc: std.mem.Allocator, - _id: ?u16, - scanner: *std.json.Scanner, + msg: *IncomingMessage, ctx: *Ctx, ) ![]const u8 { - // input const Params = struct { url: []const u8, @@ -261,13 +247,12 @@ fn navigate( frameId: ?[]const u8 = null, referrerPolicy: ?[]const u8 = null, // TODO: enum }; - const msg = try getMsg(alloc, _id, Params, scanner); - std.debug.assert(msg.sessionID != null); - log.debug("Req > id {d}, method {s}", .{ msg.id, "page.navigate" }); - const params = msg.params.?; + const input = try msg.getInput(alloc, Params); + std.debug.assert(input.sessionId != null); + log.debug("Req > id {d}, method {s}", .{ input.id, "page.navigate" }); // change state - ctx.state.url = params.url; + ctx.state.url = input.params.url; // TODO: hard coded ID ctx.state.loaderID = "AF8667A203C5392DBE9AC290044AA4C2"; @@ -289,7 +274,7 @@ fn navigate( "Page.frameStartedLoading", FrameStartedLoading, frame_started_loading, - msg.sessionID, + input.sessionId, ); if (ctx.state.page_life_cycle_events) { life_event.name = "init"; @@ -300,7 +285,7 @@ fn navigate( "Page.lifecycleEvent", LifecycleEvent, life_event, - msg.sessionID, + input.sessionId, ); } @@ -330,14 +315,14 @@ fn navigate( .frameId = ctx.state.frameID, .loaderId = ctx.state.loaderID, }; - const res = try result(alloc, msg.id, Resp, resp, msg.sessionID); + const res = try result(alloc, input.id, Resp, resp, input.sessionId); try server.sendAsync(ctx, res); // TODO: at this point do we need async the following actions to be async? // Send Runtime.executionContextsCleared event // TODO: noop event, we have no env context at this point, is it necesarry? - try sendEvent(alloc, ctx, "Runtime.executionContextsCleared", void, {}, msg.sessionID); + try sendEvent(alloc, ctx, "Runtime.executionContextsCleared", void, {}, input.sessionId); // Launch navigate const p = try ctx.browser.session.createPage(); @@ -349,7 +334,7 @@ fn navigate( .{ctx.state.frameID}, ); defer alloc.free(auxData); - try p.navigate(params.url, auxData); + try p.navigate(input.params.url, auxData); // Events @@ -364,7 +349,7 @@ fn navigate( "Page.lifecycleEvent", LifecycleEvent, life_event, - msg.sessionID, + input.sessionId, ); } @@ -388,7 +373,7 @@ fn navigate( "Page.frameNavigated", FrameNavigated, frame_navigated, - msg.sessionID, + input.sessionId, ); // domContentEventFired event @@ -400,7 +385,7 @@ fn navigate( "Page.domContentEventFired", cdp.TimestampEvent, ts_event, - msg.sessionID, + input.sessionId, ); // lifecycle DOMContentLoaded event @@ -414,7 +399,7 @@ fn navigate( "Page.lifecycleEvent", LifecycleEvent, life_event, - msg.sessionID, + input.sessionId, ); } @@ -427,7 +412,7 @@ fn navigate( "Page.loadEventFired", cdp.TimestampEvent, ts_event, - msg.sessionID, + input.sessionId, ); // lifecycle DOMContentLoaded event @@ -441,7 +426,7 @@ fn navigate( "Page.lifecycleEvent", LifecycleEvent, life_event, - msg.sessionID, + input.sessionId, ); } @@ -453,7 +438,7 @@ fn navigate( "Page.frameStoppedLoading", FrameStoppedLoading, .{ .frameId = ctx.state.frameID }, - msg.sessionID, + input.sessionId, ); return ""; diff --git a/src/cdp/performance.zig b/src/cdp/performance.zig index ce8e670d..ebb602d5 100644 --- a/src/cdp/performance.zig +++ b/src/cdp/performance.zig @@ -22,7 +22,7 @@ const server = @import("../server.zig"); const Ctx = server.Ctx; const cdp = @import("cdp.zig"); const result = cdp.result; -const getMsg = cdp.getMsg; +const IncomingMessage = @import("msg.zig").IncomingMessage; const log = std.log.scoped(.cdp); @@ -32,29 +32,26 @@ const Methods = enum { pub fn performance( alloc: std.mem.Allocator, - id: ?u16, + msg: *IncomingMessage, action: []const u8, - scanner: *std.json.Scanner, ctx: *Ctx, ) ![]const u8 { const method = std.meta.stringToEnum(Methods, action) orelse return error.UnknownMethod; return switch (method) { - .enable => enable(alloc, id, scanner, ctx), + .enable => enable(alloc, msg, ctx), }; } fn enable( alloc: std.mem.Allocator, - _id: ?u16, - scanner: *std.json.Scanner, + msg: *IncomingMessage, _: *Ctx, ) ![]const u8 { - // input - const msg = try getMsg(alloc, _id, void, scanner); - log.debug("Req > id {d}, method {s}", .{ msg.id, "performance.enable" }); + const input = try msg.getInput(alloc, void); + log.debug("Req > id {d}, method {s}", .{ input.id, "performance.enable" }); - return result(alloc, msg.id, null, null, msg.sessionID); + return result(alloc, input.id, null, null, input.sessionId); } diff --git a/src/cdp/runtime.zig b/src/cdp/runtime.zig index d2723044..03f1e068 100644 --- a/src/cdp/runtime.zig +++ b/src/cdp/runtime.zig @@ -25,7 +25,7 @@ const server = @import("../server.zig"); const Ctx = server.Ctx; const cdp = @import("cdp.zig"); const result = cdp.result; -const getMsg = cdp.getMsg; +const IncomingMessage = @import("msg.zig").IncomingMessage; const stringify = cdp.stringify; const log = std.log.scoped(.cdp); @@ -41,27 +41,23 @@ const Methods = enum { pub fn runtime( alloc: std.mem.Allocator, - id: ?u16, + msg: *IncomingMessage, action: []const u8, - scanner: *std.json.Scanner, - s: []const u8, ctx: *Ctx, ) ![]const u8 { const method = std.meta.stringToEnum(Methods, action) orelse // NOTE: we could send it anyway to the JS runtime but it's good to check it return error.UnknownMethod; return switch (method) { - .runIfWaitingForDebugger => runIfWaitingForDebugger(alloc, id, scanner, ctx), - else => sendInspector(alloc, method, id, s, scanner, ctx), + .runIfWaitingForDebugger => runIfWaitingForDebugger(alloc, msg, ctx), + else => sendInspector(alloc, method, msg, ctx), }; } fn sendInspector( alloc: std.mem.Allocator, method: Methods, - _id: ?u16, - s: []const u8, - scanner: *std.json.Scanner, + msg: *IncomingMessage, ctx: *Ctx, ) ![]const u8 { @@ -69,8 +65,8 @@ fn sendInspector( if (std.log.defaultLogEnabled(.debug)) { // input - var script: ?[]const u8 = null; var id: u16 = undefined; + var script: ?[]const u8 = null; if (method == .evaluate) { const Params = struct { @@ -81,11 +77,10 @@ fn sendInspector( userGesture: ?bool = null, }; - const msg = try getMsg(alloc, _id, Params, scanner); - log.debug("Req > id {d}, method {s} (script saved on cache)", .{ msg.id, "runtime.evaluate" }); - const params = msg.params.?; - script = params.expression; - id = msg.id; + const input = try msg.getInput(alloc, Params); + log.debug("Req > id {d}, method {s} (script saved on cache)", .{ input.id, "runtime.evaluate" }); + script = input.params.expression; + id = input.id; } else if (method == .callFunctionOn) { const Params = struct { functionDeclaration: []const u8, @@ -100,11 +95,10 @@ fn sendInspector( userGesture: ?bool = null, }; - const msg = try getMsg(alloc, _id, Params, scanner); - log.debug("Req > id {d}, method {s} (script saved on cache)", .{ msg.id, "runtime.callFunctionOn" }); - const params = msg.params.?; - script = params.functionDeclaration; - id = msg.id; + const input = try msg.getInput(alloc, Params); + log.debug("Req > id {d}, method {s} (script saved on cache)", .{ input.id, "runtime.callFunctionOn" }); + script = input.params.functionDeclaration; + id = input.id; } if (script) |src| { @@ -115,12 +109,12 @@ fn sendInspector( // remove awaitPromise true params // TODO: delete when Promise are correctly handled by zig-js-runtime if (method == .callFunctionOn or method == .evaluate) { - const buf = try alloc.alloc(u8, s.len + 1); + const buf = try alloc.alloc(u8, msg.json.len + 1); defer alloc.free(buf); - _ = std.mem.replace(u8, s, "\"awaitPromise\":true", "\"awaitPromise\":false", buf); + _ = std.mem.replace(u8, msg.json, "\"awaitPromise\":true", "\"awaitPromise\":false", buf); ctx.sendInspector(buf); } else { - ctx.sendInspector(s); + ctx.sendInspector(msg.json); } return ""; } @@ -166,14 +160,11 @@ pub fn executionContextCreated( // should we be passing this also to the JS Inspector? fn runIfWaitingForDebugger( alloc: std.mem.Allocator, - _id: ?u16, - scanner: *std.json.Scanner, + msg: *IncomingMessage, _: *Ctx, ) ![]const u8 { + const input = try msg.getInput(alloc, void); + log.debug("Req > id {d}, method {s}", .{ input.id, "runtime.runIfWaitingForDebugger" }); - // input - const msg = try getMsg(alloc, _id, void, scanner); - log.debug("Req > id {d}, method {s}", .{ msg.id, "runtime.runIfWaitingForDebugger" }); - - return result(alloc, msg.id, null, null, msg.sessionID); + return result(alloc, input.id, null, null, input.sessionId); } diff --git a/src/cdp/target.zig b/src/cdp/target.zig index 1ef9b86b..ce80e74b 100644 --- a/src/cdp/target.zig +++ b/src/cdp/target.zig @@ -22,8 +22,8 @@ const server = @import("../server.zig"); const Ctx = server.Ctx; const cdp = @import("cdp.zig"); const result = cdp.result; -const getMsg = cdp.getMsg; const stringify = cdp.stringify; +const IncomingMessage = @import("msg.zig").IncomingMessage; const log = std.log.scoped(.cdp); @@ -41,23 +41,22 @@ const Methods = enum { pub fn target( alloc: std.mem.Allocator, - id: ?u16, + msg: *IncomingMessage, action: []const u8, - scanner: *std.json.Scanner, ctx: *Ctx, ) ![]const u8 { const method = std.meta.stringToEnum(Methods, action) orelse return error.UnknownMethod; return switch (method) { - .setDiscoverTargets => setDiscoverTargets(alloc, id, scanner, ctx), - .setAutoAttach => setAutoAttach(alloc, id, scanner, ctx), - .attachToTarget => attachToTarget(alloc, id, scanner, ctx), - .getTargetInfo => getTargetInfo(alloc, id, scanner, ctx), - .getBrowserContexts => getBrowserContexts(alloc, id, scanner, ctx), - .createBrowserContext => createBrowserContext(alloc, id, scanner, ctx), - .disposeBrowserContext => disposeBrowserContext(alloc, id, scanner, ctx), - .createTarget => createTarget(alloc, id, scanner, ctx), - .closeTarget => closeTarget(alloc, id, scanner, ctx), + .setDiscoverTargets => setDiscoverTargets(alloc, msg, ctx), + .setAutoAttach => setAutoAttach(alloc, msg, ctx), + .attachToTarget => attachToTarget(alloc, msg, ctx), + .getTargetInfo => getTargetInfo(alloc, msg, ctx), + .getBrowserContexts => getBrowserContexts(alloc, msg, ctx), + .createBrowserContext => createBrowserContext(alloc, msg, ctx), + .disposeBrowserContext => disposeBrowserContext(alloc, msg, ctx), + .createTarget => createTarget(alloc, msg, ctx), + .closeTarget => closeTarget(alloc, msg, ctx), }; } @@ -69,17 +68,15 @@ const BrowserContextID = "65618675CB7D3585A95049E9DFE95EA9"; // TODO: noop method fn setDiscoverTargets( alloc: std.mem.Allocator, - _id: ?u16, - scanner: *std.json.Scanner, + msg: *IncomingMessage, _: *Ctx, ) ![]const u8 { - // input - const msg = try getMsg(alloc, _id, void, scanner); - log.debug("Req > id {d}, method {s}", .{ msg.id, "target.setDiscoverTargets" }); + const input = try msg.getInput(alloc, void); + log.debug("Req > id {d}, method {s}", .{ input.id, "target.setDiscoverTargets" }); // output - return result(alloc, msg.id, null, null, msg.sessionID); + return result(alloc, input.id, null, null, input.sessionId); } const AttachToTarget = struct { @@ -104,11 +101,9 @@ const TargetFilter = struct { // TODO: noop method fn setAutoAttach( alloc: std.mem.Allocator, - _id: ?u16, - scanner: *std.json.Scanner, + msg: *IncomingMessage, ctx: *Ctx, ) ![]const u8 { - // input const Params = struct { autoAttach: bool, @@ -116,11 +111,11 @@ fn setAutoAttach( flatten: bool = true, filter: ?[]TargetFilter = null, }; - const msg = try getMsg(alloc, _id, Params, scanner); - log.debug("Req > id {d}, method {s}", .{ msg.id, "target.setAutoAttach" }); + const input = try msg.getInput(alloc, Params); + log.debug("Req > id {d}, method {s}", .{ input.id, "target.setAutoAttach" }); // attachedToTarget event - if (msg.sessionID == null) { + if (input.sessionId == null) { const attached = AttachToTarget{ .sessionId = cdp.BrowserSessionID, .targetInfo = .{ @@ -134,14 +129,13 @@ fn setAutoAttach( } // output - return result(alloc, msg.id, null, null, msg.sessionID); + return result(alloc, input.id, null, null, input.sessionId); } // TODO: noop method fn attachToTarget( alloc: std.mem.Allocator, - _id: ?u16, - scanner: *std.json.Scanner, + msg: *IncomingMessage, ctx: *Ctx, ) ![]const u8 { @@ -150,11 +144,11 @@ fn attachToTarget( targetId: []const u8, flatten: bool = true, }; - const msg = try getMsg(alloc, _id, Params, scanner); - log.debug("Req > id {d}, method {s}", .{ msg.id, "target.setAutoAttach" }); + const input = try msg.getInput(alloc, Params); + log.debug("Req > id {d}, method {s}", .{ input.id, "target.attachToTarget" }); // attachedToTarget event - if (msg.sessionID == null) { + if (input.sessionId == null) { const attached = AttachToTarget{ .sessionId = cdp.BrowserSessionID, .targetInfo = .{ @@ -172,24 +166,22 @@ fn attachToTarget( sessionId: []const u8, }; const output = SessionId{ - .sessionId = msg.sessionID orelse BrowserContextID, + .sessionId = input.sessionId orelse BrowserContextID, }; - return result(alloc, msg.id, SessionId, output, null); + return result(alloc, input.id, SessionId, output, null); } fn getTargetInfo( alloc: std.mem.Allocator, - _id: ?u16, - scanner: *std.json.Scanner, + msg: *IncomingMessage, _: *Ctx, ) ![]const u8 { - // input const Params = struct { targetId: ?[]const u8 = null, }; - const msg = try getMsg(alloc, _id, Params, scanner); - log.debug("Req > id {d}, method {s}", .{ msg.id, "target.getTargetInfo" }); + const input = try msg.getInput(alloc, Params); + log.debug("Req > id {d}, method {s}", .{ input.id, "target.getTargetInfo" }); // output const TargetInfo = struct { @@ -208,7 +200,7 @@ fn getTargetInfo( .targetId = BrowserTargetID, .type = "browser", }; - return result(alloc, msg.id, TargetInfo, targetInfo, null); + return result(alloc, input.id, TargetInfo, targetInfo, null); } // Browser context are not handled and not in the roadmap for now @@ -217,14 +209,12 @@ fn getTargetInfo( // TODO: noop method fn getBrowserContexts( alloc: std.mem.Allocator, - _id: ?u16, - scanner: *std.json.Scanner, + msg: *IncomingMessage, ctx: *Ctx, ) ![]const u8 { - // input - const msg = try getMsg(alloc, _id, void, scanner); - log.debug("Req > id {d}, method {s}", .{ msg.id, "target.getBrowserContexts" }); + const input = try msg.getInput(alloc, void); + log.debug("Req > id {d}, method {s}", .{ input.id, "target.getBrowserContexts" }); // ouptut const Resp = struct { @@ -238,7 +228,7 @@ fn getBrowserContexts( const contextIDs = [0][]const u8{}; resp = .{ .browserContextIds = &contextIDs }; } - return result(alloc, msg.id, Resp, resp, null); + return result(alloc, input.id, Resp, resp, null); } const ContextID = "22648B09EDCCDD11109E2D4FEFBE4F89"; @@ -246,11 +236,9 @@ const ContextID = "22648B09EDCCDD11109E2D4FEFBE4F89"; // TODO: noop method fn createBrowserContext( alloc: std.mem.Allocator, - _id: ?u16, - scanner: *std.json.Scanner, + msg: *IncomingMessage, ctx: *Ctx, ) ![]const u8 { - // input const Params = struct { disposeOnDetach: bool = false, @@ -258,8 +246,8 @@ fn createBrowserContext( proxyBypassList: ?[]const u8 = null, originsWithUniversalNetworkAccess: ?[][]const u8 = null, }; - const msg = try getMsg(alloc, _id, Params, scanner); - log.debug("Req > id {d}, method {s}", .{ msg.id, "target.createBrowserContext" }); + const input = try msg.getInput(alloc, Params); + log.debug("Req > id {d}, method {s}", .{ input.id, "target.createBrowserContext" }); ctx.state.contextID = ContextID; @@ -279,25 +267,23 @@ fn createBrowserContext( try writer.writeAll(" }"); } }; - return result(alloc, msg.id, Resp, Resp{}, msg.sessionID); + return result(alloc, input.id, Resp, Resp{}, input.sessionId); } fn disposeBrowserContext( alloc: std.mem.Allocator, - _id: ?u16, - scanner: *std.json.Scanner, + msg: *IncomingMessage, ctx: *Ctx, ) ![]const u8 { - // input const Params = struct { browserContextId: []const u8, }; - const msg = try getMsg(alloc, _id, Params, scanner); - log.debug("Req > id {d}, method {s}", .{ msg.id, "target.disposeBrowserContext" }); + const input = try msg.getInput(alloc, Params); + log.debug("Req > id {d}, method {s}", .{ input.id, "target.disposeBrowserContext" }); // output - const res = try result(alloc, msg.id, null, .{}, null); + const res = try result(alloc, input.id, null, .{}, null); try server.sendAsync(ctx, res); return error.DisposeBrowserContext; @@ -309,11 +295,9 @@ const LoaderID = "DD4A76F842AA389647D702B4D805F49A"; fn createTarget( alloc: std.mem.Allocator, - _id: ?u16, - scanner: *std.json.Scanner, + msg: *IncomingMessage, ctx: *Ctx, ) ![]const u8 { - // input const Params = struct { url: []const u8, @@ -325,8 +309,8 @@ fn createTarget( background: bool = false, forTab: ?bool = null, }; - const msg = try getMsg(alloc, _id, Params, scanner); - log.debug("Req > id {d}, method {s}", .{ msg.id, "target.createTarget" }); + const input = try msg.getInput(alloc, Params); + log.debug("Req > id {d}, method {s}", .{ input.id, "target.createTarget" }); // change CDP state ctx.state.frameID = TargetID; @@ -346,7 +330,7 @@ fn createTarget( }, .waitingForDebugger = true, }; - try cdp.sendEvent(alloc, ctx, "Target.attachedToTarget", AttachToTarget, attached, msg.sessionID); + try cdp.sendEvent(alloc, ctx, "Target.attachedToTarget", AttachToTarget, attached, input.sessionId); // output const Resp = struct { @@ -364,28 +348,26 @@ fn createTarget( try writer.writeAll(" }"); } }; - return result(alloc, msg.id, Resp, Resp{}, msg.sessionID); + return result(alloc, input.id, Resp, Resp{}, input.sessionId); } fn closeTarget( alloc: std.mem.Allocator, - _id: ?u16, - scanner: *std.json.Scanner, + msg: *IncomingMessage, ctx: *Ctx, ) ![]const u8 { - // input const Params = struct { targetId: []const u8, }; - const msg = try getMsg(alloc, _id, Params, scanner); - log.debug("Req > id {d}, method {s}", .{ msg.id, "target.closeTarget" }); + const input = try msg.getInput(alloc, Params); + log.debug("Req > id {d}, method {s}", .{ input.id, "target.closeTarget" }); // output const Resp = struct { success: bool = true, }; - const res = try result(alloc, msg.id, Resp, Resp{}, null); + const res = try result(alloc, input.id, Resp, Resp{}, null); try server.sendAsync(ctx, res); // Inspector.detached event @@ -398,7 +380,7 @@ fn closeTarget( "Inspector.detached", InspectorDetached, .{}, - msg.sessionID orelse cdp.ContextSessionID, + input.sessionId orelse cdp.ContextSessionID, ); // detachedFromTarget event @@ -412,8 +394,8 @@ fn closeTarget( "Target.detachedFromTarget", TargetDetached, .{ - .sessionId = msg.sessionID orelse cdp.ContextSessionID, - .targetId = msg.params.?.targetId, + .sessionId = input.sessionId orelse cdp.ContextSessionID, + .targetId = input.params.targetId, }, null, ); From 50949425604ab53f22377fe18692778148f12c26 Mon Sep 17 00:00:00 2001 From: Pierre Tachoire Date: Tue, 12 Nov 2024 11:07:56 +0100 Subject: [PATCH 2/2] cdp: add msg tests into zig build test --- src/run_tests.zig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/run_tests.zig b/src/run_tests.zig index a56d76dd..8a5be4a5 100644 --- a/src/run_tests.zig +++ b/src/run_tests.zig @@ -321,6 +321,8 @@ test { const queryTest = @import("url/query.zig"); std.testing.refAllDecls(queryTest); + + std.testing.refAllDecls(@import("cdp/msg.zig")); } fn testJSRuntime(alloc: std.mem.Allocator) !void {