Merge pull request #286 from lightpanda-io/cdp-refacto-input

cdp: refacto message JSON read
This commit is contained in:
Francis Bouvier
2024-11-15 01:03:53 +01:00
committed by GitHub
12 changed files with 433 additions and 409 deletions

View File

@@ -22,7 +22,7 @@ const server = @import("../server.zig");
const Ctx = server.Ctx; const Ctx = server.Ctx;
const cdp = @import("cdp.zig"); const cdp = @import("cdp.zig");
const result = cdp.result; const result = cdp.result;
const getMsg = cdp.getMsg; const IncomingMessage = @import("msg.zig").IncomingMessage;
const log = std.log.scoped(.cdp); const log = std.log.scoped(.cdp);
@@ -35,18 +35,17 @@ const Methods = enum {
pub fn browser( pub fn browser(
alloc: std.mem.Allocator, alloc: std.mem.Allocator,
id: ?u16, msg: *IncomingMessage,
action: []const u8, action: []const u8,
scanner: *std.json.Scanner,
ctx: *Ctx, ctx: *Ctx,
) ![]const u8 { ) ![]const u8 {
const method = std.meta.stringToEnum(Methods, action) orelse const method = std.meta.stringToEnum(Methods, action) orelse
return error.UnknownMethod; return error.UnknownMethod;
return switch (method) { return switch (method) {
.getVersion => getVersion(alloc, id, scanner, ctx), .getVersion => getVersion(alloc, msg, ctx),
.setDownloadBehavior => setDownloadBehavior(alloc, id, scanner, ctx), .setDownloadBehavior => setDownloadBehavior(alloc, msg, ctx),
.getWindowForTarget => getWindowForTarget(alloc, id, scanner, ctx), .getWindowForTarget => getWindowForTarget(alloc, msg, ctx),
.setWindowBounds => setWindowBounds(alloc, id, scanner, ctx), .setWindowBounds => setWindowBounds(alloc, msg, ctx),
}; };
} }
@@ -59,14 +58,12 @@ const JsVersion = "12.4.254.8";
fn getVersion( fn getVersion(
alloc: std.mem.Allocator, alloc: std.mem.Allocator,
_id: ?u16, msg: *IncomingMessage,
scanner: *std.json.Scanner,
_: *Ctx, _: *Ctx,
) ![]const u8 { ) ![]const u8 {
// input // input
const msg = try getMsg(alloc, _id, void, scanner); const input = try msg.getInput(alloc, void);
log.debug("Req > id {d}, method {s}", .{ msg.id, "browser.getVersion" }); log.debug("Req > id {d}, method {s}", .{ input.id, "browser.getVersion" });
// ouput // ouput
const Res = struct { const Res = struct {
@@ -76,17 +73,15 @@ fn getVersion(
userAgent: []const u8 = UserAgent, userAgent: []const u8 = UserAgent,
jsVersion: []const u8 = JsVersion, jsVersion: []const u8 = JsVersion,
}; };
return result(alloc, msg.id, Res, .{}, null); return result(alloc, input.id, Res, .{}, null);
} }
// TODO: noop method // TODO: noop method
fn setDownloadBehavior( fn setDownloadBehavior(
alloc: std.mem.Allocator, alloc: std.mem.Allocator,
_id: ?u16, msg: *IncomingMessage,
scanner: *std.json.Scanner,
_: *Ctx, _: *Ctx,
) ![]const u8 { ) ![]const u8 {
// input // input
const Params = struct { const Params = struct {
behavior: []const u8, behavior: []const u8,
@@ -94,11 +89,11 @@ fn setDownloadBehavior(
downloadPath: ?[]const u8 = null, downloadPath: ?[]const u8 = null,
eventsEnabled: ?bool = null, eventsEnabled: ?bool = null,
}; };
const msg = try getMsg(alloc, _id, Params, scanner); const input = try msg.getInput(alloc, Params);
log.debug("REQ > id {d}, method {s}", .{ msg.id, "browser.setDownloadBehavior" }); log.debug("REQ > id {d}, method {s}", .{ input.id, "browser.setDownloadBehavior" });
// output // output
return result(alloc, msg.id, null, null, null); return result(alloc, input.id, null, null, null);
} }
// TODO: hard coded ID // TODO: hard coded ID
@@ -106,8 +101,7 @@ const DevToolsWindowID = 1923710101;
fn getWindowForTarget( fn getWindowForTarget(
alloc: std.mem.Allocator, alloc: std.mem.Allocator,
_id: ?u16, msg: *IncomingMessage,
scanner: *std.json.Scanner,
_: *Ctx, _: *Ctx,
) ![]const u8 { ) ![]const u8 {
@@ -115,9 +109,9 @@ fn getWindowForTarget(
const Params = struct { const Params = struct {
targetId: ?[]const u8 = null, targetId: ?[]const u8 = null,
}; };
const msg = try cdp.getMsg(alloc, _id, ?Params, scanner); const input = try msg.getInput(alloc, Params);
std.debug.assert(msg.sessionID != null); std.debug.assert(input.sessionId != null);
log.debug("Req > id {d}, method {s}", .{ msg.id, "browser.getWindowForTarget" }); log.debug("Req > id {d}, method {s}", .{ input.id, "browser.getWindowForTarget" });
// output // output
const Resp = struct { const Resp = struct {
@@ -130,21 +124,20 @@ fn getWindowForTarget(
windowState: []const u8 = "normal", 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 // TODO: noop method
fn setWindowBounds( fn setWindowBounds(
alloc: std.mem.Allocator, alloc: std.mem.Allocator,
_id: ?u16, msg: *IncomingMessage,
scanner: *std.json.Scanner,
_: *Ctx, _: *Ctx,
) ![]const u8 { ) ![]const u8 {
const input = try msg.getInput(alloc, void);
// input // input
const msg = try cdp.getMsg(alloc, _id, void, scanner); log.debug("Req > id {d}, method {s}", .{ input.id, "browser.setWindowBounds" });
log.debug("Req > id {d}, method {s}", .{ msg.id, "browser.setWindowBounds" });
// output // output
return result(alloc, msg.id, null, null, msg.sessionID); return result(alloc, input.id, null, null, input.sessionId);
} }

View File

@@ -30,6 +30,7 @@ const network = @import("network.zig").network;
const emulation = @import("emulation.zig").emulation; const emulation = @import("emulation.zig").emulation;
const fetch = @import("fetch.zig").fetch; const fetch = @import("fetch.zig").fetch;
const performance = @import("performance.zig").performance; const performance = @import("performance.zig").performance;
const IncomingMessage = @import("msg.zig").IncomingMessage;
const log_cdp = std.log.scoped(.cdp); const log_cdp = std.log.scoped(.cdp);
@@ -70,51 +71,29 @@ pub fn do(
ctx: *Ctx, ctx: *Ctx,
) ![]const u8 { ) ![]const u8 {
// JSON scanner // incoming message parser
var scanner = std.json.Scanner.initCompleteInput(alloc, s); var msg = IncomingMessage.init(alloc, s);
defer scanner.deinit(); defer msg.deinit();
std.debug.assert(try scanner.next() == .object_begin); const method = try msg.getMethod();
// 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;
// retrieve domain from method // 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 const domain = std.meta.stringToEnum(Domains, iter.first()) orelse
return error.UnknonwDomain; return error.UnknonwDomain;
// select corresponding domain // select corresponding domain
const action = iter.next() orelse return error.BadMethod; const action = iter.next() orelse return error.BadMethod;
return switch (domain) { return switch (domain) {
.Browser => browser(alloc, id, action, &scanner, ctx), .Browser => browser(alloc, &msg, action, ctx),
.Target => target(alloc, id, action, &scanner, ctx), .Target => target(alloc, &msg, action, ctx),
.Page => page(alloc, id, action, &scanner, ctx), .Page => page(alloc, &msg, action, ctx),
.Log => log(alloc, id, action, &scanner, ctx), .Log => log(alloc, &msg, action, ctx),
.Runtime => runtime(alloc, id, action, &scanner, s, ctx), .Runtime => runtime(alloc, &msg, action, ctx),
.Network => network(alloc, id, action, &scanner, ctx), .Network => network(alloc, &msg, action, ctx),
.Emulation => emulation(alloc, id, action, &scanner, ctx), .Emulation => emulation(alloc, &msg, action, ctx),
.Fetch => fetch(alloc, id, action, &scanner, ctx), .Fetch => fetch(alloc, &msg, action, ctx),
.Performance => performance(alloc, id, action, &scanner, ctx), .Performance => performance(alloc, &msg, action, ctx),
}; };
} }
@@ -133,14 +112,6 @@ pub const State = struct {
// Utils // 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( pub fn dumpFile(
alloc: std.mem.Allocator, alloc: std.mem.Allocator,
id: u16, id: u16,
@@ -158,10 +129,6 @@ pub fn dumpFile(
defer alloc.free(p); 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 // caller owns the slice returned
pub fn stringify(alloc: std.mem.Allocator, res: anytype) ![]const u8 { pub fn stringify(alloc: std.mem.Allocator, res: anytype) ![]const u8 {
var out = std.ArrayList(u8).init(alloc); var out = std.ArrayList(u8).init(alloc);
@@ -229,97 +196,6 @@ pub fn sendEvent(
try server.sendAsync(ctx, event_msg); 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 // Common
// ------ // ------

View File

@@ -22,8 +22,8 @@ const server = @import("../server.zig");
const Ctx = server.Ctx; const Ctx = server.Ctx;
const cdp = @import("cdp.zig"); const cdp = @import("cdp.zig");
const result = cdp.result; const result = cdp.result;
const getMsg = cdp.getMsg;
const stringify = cdp.stringify; const stringify = cdp.stringify;
const IncomingMessage = @import("msg.zig").IncomingMessage;
const log = std.log.scoped(.cdp); const log = std.log.scoped(.cdp);
@@ -36,18 +36,17 @@ const Methods = enum {
pub fn emulation( pub fn emulation(
alloc: std.mem.Allocator, alloc: std.mem.Allocator,
id: ?u16, msg: *IncomingMessage,
action: []const u8, action: []const u8,
scanner: *std.json.Scanner,
ctx: *Ctx, ctx: *Ctx,
) ![]const u8 { ) ![]const u8 {
const method = std.meta.stringToEnum(Methods, action) orelse const method = std.meta.stringToEnum(Methods, action) orelse
return error.UnknownMethod; return error.UnknownMethod;
return switch (method) { return switch (method) {
.setEmulatedMedia => setEmulatedMedia(alloc, id, scanner, ctx), .setEmulatedMedia => setEmulatedMedia(alloc, msg, ctx),
.setFocusEmulationEnabled => setFocusEmulationEnabled(alloc, id, scanner, ctx), .setFocusEmulationEnabled => setFocusEmulationEnabled(alloc, msg, ctx),
.setDeviceMetricsOverride => setDeviceMetricsOverride(alloc, id, scanner, ctx), .setDeviceMetricsOverride => setDeviceMetricsOverride(alloc, msg, ctx),
.setTouchEmulationEnabled => setTouchEmulationEnabled(alloc, id, scanner, ctx), .setTouchEmulationEnabled => setTouchEmulationEnabled(alloc, msg, ctx),
}; };
} }
@@ -59,8 +58,7 @@ const MediaFeature = struct {
// TODO: noop method // TODO: noop method
fn setEmulatedMedia( fn setEmulatedMedia(
alloc: std.mem.Allocator, alloc: std.mem.Allocator,
_id: ?u16, msg: *IncomingMessage,
scanner: *std.json.Scanner,
_: *Ctx, _: *Ctx,
) ![]const u8 { ) ![]const u8 {
@@ -69,57 +67,52 @@ fn setEmulatedMedia(
media: ?[]const u8 = null, media: ?[]const u8 = null,
features: ?[]MediaFeature = null, features: ?[]MediaFeature = null,
}; };
const msg = try getMsg(alloc, _id, Params, scanner); const input = try msg.getInput(alloc, Params);
log.debug("Req > id {d}, method {s}", .{ msg.id, "emulation.setEmulatedMedia" }); log.debug("Req > id {d}, method {s}", .{ input.id, "emulation.setEmulatedMedia" });
// output // output
return result(alloc, msg.id, null, null, msg.sessionID); return result(alloc, input.id, null, null, input.sessionId);
} }
// TODO: noop method // TODO: noop method
fn setFocusEmulationEnabled( fn setFocusEmulationEnabled(
alloc: std.mem.Allocator, alloc: std.mem.Allocator,
_id: ?u16, msg: *IncomingMessage,
scanner: *std.json.Scanner,
_: *Ctx, _: *Ctx,
) ![]const u8 { ) ![]const u8 {
// input // input
const Params = struct { const Params = struct {
enabled: bool, enabled: bool,
}; };
const msg = try getMsg(alloc, _id, Params, scanner); const input = try msg.getInput(alloc, Params);
log.debug("Req > id {d}, method {s}", .{ msg.id, "emulation.setFocusEmulationEnabled" }); log.debug("Req > id {d}, method {s}", .{ input.id, "emulation.setFocusEmulationEnabled" });
// output // output
return result(alloc, msg.id, null, null, msg.sessionID); return result(alloc, input.id, null, null, input.sessionId);
} }
// TODO: noop method // TODO: noop method
fn setDeviceMetricsOverride( fn setDeviceMetricsOverride(
alloc: std.mem.Allocator, alloc: std.mem.Allocator,
_id: ?u16, msg: *IncomingMessage,
scanner: *std.json.Scanner,
_: *Ctx, _: *Ctx,
) ![]const u8 { ) ![]const u8 {
// input // input
const msg = try cdp.getMsg(alloc, _id, void, scanner); const input = try msg.getInput(alloc, void);
log.debug("Req > id {d}, method {s}", .{ msg.id, "emulation.setDeviceMetricsOverride" }); log.debug("Req > id {d}, method {s}", .{ input.id, "emulation.setDeviceMetricsOverride" });
// output // output
return result(alloc, msg.id, null, null, msg.sessionID); return result(alloc, input.id, null, null, input.sessionId);
} }
// TODO: noop method // TODO: noop method
fn setTouchEmulationEnabled( fn setTouchEmulationEnabled(
alloc: std.mem.Allocator, alloc: std.mem.Allocator,
_id: ?u16, msg: *IncomingMessage,
scanner: *std.json.Scanner,
_: *Ctx, _: *Ctx,
) ![]const u8 { ) ![]const u8 {
const msg = try cdp.getMsg(alloc, _id, void, scanner); const input = try msg.getInput(alloc, void);
log.debug("Req > id {d}, method {s}", .{ msg.id, "emulation.setTouchEmulationEnabled" }); 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);
} }

View File

@@ -22,7 +22,7 @@ const server = @import("../server.zig");
const Ctx = server.Ctx; const Ctx = server.Ctx;
const cdp = @import("cdp.zig"); const cdp = @import("cdp.zig");
const result = cdp.result; const result = cdp.result;
const getMsg = cdp.getMsg; const IncomingMessage = @import("msg.zig").IncomingMessage;
const log = std.log.scoped(.cdp); const log = std.log.scoped(.cdp);
@@ -32,28 +32,26 @@ const Methods = enum {
pub fn fetch( pub fn fetch(
alloc: std.mem.Allocator, alloc: std.mem.Allocator,
id: ?u16, msg: *IncomingMessage,
action: []const u8, action: []const u8,
scanner: *std.json.Scanner,
ctx: *Ctx, ctx: *Ctx,
) ![]const u8 { ) ![]const u8 {
const method = std.meta.stringToEnum(Methods, action) orelse const method = std.meta.stringToEnum(Methods, action) orelse
return error.UnknownMethod; return error.UnknownMethod;
return switch (method) { return switch (method) {
.disable => disable(alloc, id, scanner, ctx), .disable => disable(alloc, msg, ctx),
}; };
} }
// TODO: noop method // TODO: noop method
fn disable( fn disable(
alloc: std.mem.Allocator, alloc: std.mem.Allocator,
_id: ?u16, msg: *IncomingMessage,
scanner: *std.json.Scanner,
_: *Ctx, _: *Ctx,
) ![]const u8 { ) ![]const u8 {
const msg = try getMsg(alloc, _id, void, scanner); const input = try msg.getInput(alloc, void);
log.debug("Req > id {d}, method {s}", .{ msg.id, "fetch.disable" }); 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);
} }

View File

@@ -22,7 +22,7 @@ const server = @import("../server.zig");
const Ctx = server.Ctx; const Ctx = server.Ctx;
const cdp = @import("cdp.zig"); const cdp = @import("cdp.zig");
const result = cdp.result; const result = cdp.result;
const getMsg = cdp.getMsg; const IncomingMessage = @import("msg.zig").IncomingMessage;
const stringify = cdp.stringify; const stringify = cdp.stringify;
const log_cdp = std.log.scoped(.cdp); const log_cdp = std.log.scoped(.cdp);
@@ -33,27 +33,25 @@ const Methods = enum {
pub fn log( pub fn log(
alloc: std.mem.Allocator, alloc: std.mem.Allocator,
id: ?u16, msg: *IncomingMessage,
action: []const u8, action: []const u8,
scanner: *std.json.Scanner,
ctx: *Ctx, ctx: *Ctx,
) ![]const u8 { ) ![]const u8 {
const method = std.meta.stringToEnum(Methods, action) orelse const method = std.meta.stringToEnum(Methods, action) orelse
return error.UnknownMethod; return error.UnknownMethod;
return switch (method) { return switch (method) {
.enable => enable(alloc, id, scanner, ctx), .enable => enable(alloc, msg, ctx),
}; };
} }
fn enable( fn enable(
alloc: std.mem.Allocator, alloc: std.mem.Allocator,
_id: ?u16, msg: *IncomingMessage,
scanner: *std.json.Scanner,
_: *Ctx, _: *Ctx,
) ![]const u8 { ) ![]const u8 {
const msg = try getMsg(alloc, _id, void, scanner); const input = try msg.getInput(alloc, void);
log_cdp.debug("Req > id {d}, method {s}", .{ msg.id, "log.enable" }); 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);
} }

214
src/cdp/msg.zig Normal file
View File

@@ -0,0 +1,214 @@
// Copyright (C) 2023-2024 Lightpanda (Selecy SAS)
//
// Francis Bouvier <francis@lightpanda.io>
// Pierre Tachoire <pierre@lightpanda.io>
//
// 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 <https://www.gnu.org/licenses/>.
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());
}
}

View File

@@ -22,7 +22,7 @@ const server = @import("../server.zig");
const Ctx = server.Ctx; const Ctx = server.Ctx;
const cdp = @import("cdp.zig"); const cdp = @import("cdp.zig");
const result = cdp.result; const result = cdp.result;
const getMsg = cdp.getMsg; const IncomingMessage = @import("msg.zig").IncomingMessage;
const log = std.log.scoped(.cdp); const log = std.log.scoped(.cdp);
@@ -33,45 +33,40 @@ const Methods = enum {
pub fn network( pub fn network(
alloc: std.mem.Allocator, alloc: std.mem.Allocator,
id: ?u16, msg: *IncomingMessage,
action: []const u8, action: []const u8,
scanner: *std.json.Scanner,
ctx: *Ctx, ctx: *Ctx,
) ![]const u8 { ) ![]const u8 {
const method = std.meta.stringToEnum(Methods, action) orelse const method = std.meta.stringToEnum(Methods, action) orelse
return error.UnknownMethod; return error.UnknownMethod;
return switch (method) { return switch (method) {
.enable => enable(alloc, id, scanner, ctx), .enable => enable(alloc, msg, ctx),
.setCacheDisabled => setCacheDisabled(alloc, id, scanner, ctx), .setCacheDisabled => setCacheDisabled(alloc, msg, ctx),
}; };
} }
fn enable( fn enable(
alloc: std.mem.Allocator, alloc: std.mem.Allocator,
_id: ?u16, msg: *IncomingMessage,
scanner: *std.json.Scanner,
_: *Ctx, _: *Ctx,
) ![]const u8 { ) ![]const u8 {
// input // input
const msg = try getMsg(alloc, _id, void, scanner); const input = try msg.getInput(alloc, void);
log.debug("Req > id {d}, method {s}", .{ msg.id, "network.enable" }); 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 // TODO: noop method
fn setCacheDisabled( fn setCacheDisabled(
alloc: std.mem.Allocator, alloc: std.mem.Allocator,
_id: ?u16, msg: *IncomingMessage,
scanner: *std.json.Scanner,
_: *Ctx, _: *Ctx,
) ![]const u8 { ) ![]const u8 {
// input // input
const msg = try getMsg(alloc, _id, void, scanner); const input = try msg.getInput(alloc, void);
log.debug("Req > id {d}, method {s}", .{ msg.id, "network.setCacheDisabled" }); 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);
} }

View File

@@ -22,9 +22,9 @@ const server = @import("../server.zig");
const Ctx = server.Ctx; const Ctx = server.Ctx;
const cdp = @import("cdp.zig"); const cdp = @import("cdp.zig");
const result = cdp.result; const result = cdp.result;
const getMsg = cdp.getMsg;
const stringify = cdp.stringify; const stringify = cdp.stringify;
const sendEvent = cdp.sendEvent; const sendEvent = cdp.sendEvent;
const IncomingMessage = @import("msg.zig").IncomingMessage;
const log = std.log.scoped(.cdp); const log = std.log.scoped(.cdp);
@@ -41,35 +41,32 @@ const Methods = enum {
pub fn page( pub fn page(
alloc: std.mem.Allocator, alloc: std.mem.Allocator,
id: ?u16, msg: *IncomingMessage,
action: []const u8, action: []const u8,
scanner: *std.json.Scanner,
ctx: *Ctx, ctx: *Ctx,
) ![]const u8 { ) ![]const u8 {
const method = std.meta.stringToEnum(Methods, action) orelse const method = std.meta.stringToEnum(Methods, action) orelse
return error.UnknownMethod; return error.UnknownMethod;
return switch (method) { return switch (method) {
.enable => enable(alloc, id, scanner, ctx), .enable => enable(alloc, msg, ctx),
.getFrameTree => getFrameTree(alloc, id, scanner, ctx), .getFrameTree => getFrameTree(alloc, msg, ctx),
.setLifecycleEventsEnabled => setLifecycleEventsEnabled(alloc, id, scanner, ctx), .setLifecycleEventsEnabled => setLifecycleEventsEnabled(alloc, msg, ctx),
.addScriptToEvaluateOnNewDocument => addScriptToEvaluateOnNewDocument(alloc, id, scanner, ctx), .addScriptToEvaluateOnNewDocument => addScriptToEvaluateOnNewDocument(alloc, msg, ctx),
.createIsolatedWorld => createIsolatedWorld(alloc, id, scanner, ctx), .createIsolatedWorld => createIsolatedWorld(alloc, msg, ctx),
.navigate => navigate(alloc, id, scanner, ctx), .navigate => navigate(alloc, msg, ctx),
}; };
} }
fn enable( fn enable(
alloc: std.mem.Allocator, alloc: std.mem.Allocator,
_id: ?u16, msg: *IncomingMessage,
scanner: *std.json.Scanner,
_: *Ctx, _: *Ctx,
) ![]const u8 { ) ![]const u8 {
// input // input
const msg = try getMsg(alloc, _id, void, scanner); const input = try msg.getInput(alloc, void);
log.debug("Req > id {d}, method {s}", .{ msg.id, "page.enable" }); 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 { const Frame = struct {
@@ -89,14 +86,12 @@ const Frame = struct {
fn getFrameTree( fn getFrameTree(
alloc: std.mem.Allocator, alloc: std.mem.Allocator,
_id: ?u16, msg: *IncomingMessage,
scanner: *std.json.Scanner,
ctx: *Ctx, ctx: *Ctx,
) ![]const u8 { ) ![]const u8 {
// input // input
const msg = try cdp.getMsg(alloc, _id, void, scanner); const input = try msg.getInput(alloc, void);
log.debug("Req > id {d}, method {s}", .{ msg.id, "page.getFrameTree" }); log.debug("Req > id {d}, method {s}", .{ input.id, "page.getFrameTree" });
// output // output
const FrameTree = struct { 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( fn setLifecycleEventsEnabled(
alloc: std.mem.Allocator, alloc: std.mem.Allocator,
_id: ?u16, msg: *IncomingMessage,
scanner: *std.json.Scanner,
ctx: *Ctx, ctx: *Ctx,
) ![]const u8 { ) ![]const u8 {
// input // input
const Params = struct { const Params = struct {
enabled: bool, enabled: bool,
}; };
const msg = try getMsg(alloc, _id, Params, scanner); const input = try msg.getInput(alloc, Params);
log.debug("Req > id {d}, method {s}", .{ msg.id, "page.setLifecycleEventsEnabled" }); log.debug("Req > id {d}, method {s}", .{ input.id, "page.setLifecycleEventsEnabled" });
ctx.state.page_life_cycle_events = true; ctx.state.page_life_cycle_events = true;
// output // output
return result(alloc, msg.id, null, null, msg.sessionID); return result(alloc, input.id, null, null, input.sessionId);
} }
const LifecycleEvent = struct { const LifecycleEvent = struct {
@@ -168,11 +161,9 @@ const LifecycleEvent = struct {
// TODO: hard coded method // TODO: hard coded method
fn addScriptToEvaluateOnNewDocument( fn addScriptToEvaluateOnNewDocument(
alloc: std.mem.Allocator, alloc: std.mem.Allocator,
_id: ?u16, msg: *IncomingMessage,
scanner: *std.json.Scanner,
_: *Ctx, _: *Ctx,
) ![]const u8 { ) ![]const u8 {
// input // input
const Params = struct { const Params = struct {
source: []const u8, source: []const u8,
@@ -180,8 +171,8 @@ fn addScriptToEvaluateOnNewDocument(
includeCommandLineAPI: bool = false, includeCommandLineAPI: bool = false,
runImmediately: bool = false, runImmediately: bool = false,
}; };
const msg = try getMsg(alloc, _id, Params, scanner); const input = try msg.getInput(alloc, Params);
log.debug("Req > id {d}, method {s}", .{ msg.id, "page.addScriptToEvaluateOnNewDocument" }); log.debug("Req > id {d}, method {s}", .{ input.id, "page.addScriptToEvaluateOnNewDocument" });
// output // output
const Res = struct { const Res = struct {
@@ -199,27 +190,24 @@ fn addScriptToEvaluateOnNewDocument(
try writer.writeAll(" }"); 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 // TODO: hard coded method
fn createIsolatedWorld( fn createIsolatedWorld(
alloc: std.mem.Allocator, alloc: std.mem.Allocator,
_id: ?u16, msg: *IncomingMessage,
scanner: *std.json.Scanner,
ctx: *Ctx, ctx: *Ctx,
) ![]const u8 { ) ![]const u8 {
// input // input
const Params = struct { const Params = struct {
frameId: []const u8, frameId: []const u8,
worldName: []const u8, worldName: []const u8,
grantUniveralAccess: bool, grantUniveralAccess: bool,
}; };
const msg = try getMsg(alloc, _id, Params, scanner); const input = try msg.getInput(alloc, Params);
std.debug.assert(msg.sessionID != null); std.debug.assert(input.sessionId != null);
log.debug("Req > id {d}, method {s}", .{ msg.id, "page.createIsolatedWorld" }); log.debug("Req > id {d}, method {s}", .{ input.id, "page.createIsolatedWorld" });
const params = msg.params.?;
// noop executionContextCreated event // noop executionContextCreated event
try Runtime.executionContextCreated( try Runtime.executionContextCreated(
@@ -227,15 +215,15 @@ fn createIsolatedWorld(
ctx, ctx,
0, 0,
"", "",
params.worldName, input.params.worldName,
// TODO: hard coded ID // TODO: hard coded ID
"7102379147004877974.3265385113993241162", "7102379147004877974.3265385113993241162",
.{ .{
.isDefault = false, .isDefault = false,
.type = "isolated", .type = "isolated",
.frameId = params.frameId, .frameId = input.params.frameId,
}, },
msg.sessionID, input.sessionId,
); );
// output // output
@@ -243,16 +231,14 @@ fn createIsolatedWorld(
executionContextId: u8 = 0, executionContextId: u8 = 0,
}; };
return result(alloc, msg.id, Resp, .{}, msg.sessionID); return result(alloc, input.id, Resp, .{}, input.sessionId);
} }
fn navigate( fn navigate(
alloc: std.mem.Allocator, alloc: std.mem.Allocator,
_id: ?u16, msg: *IncomingMessage,
scanner: *std.json.Scanner,
ctx: *Ctx, ctx: *Ctx,
) ![]const u8 { ) ![]const u8 {
// input // input
const Params = struct { const Params = struct {
url: []const u8, url: []const u8,
@@ -261,13 +247,12 @@ fn navigate(
frameId: ?[]const u8 = null, frameId: ?[]const u8 = null,
referrerPolicy: ?[]const u8 = null, // TODO: enum referrerPolicy: ?[]const u8 = null, // TODO: enum
}; };
const msg = try getMsg(alloc, _id, Params, scanner); const input = try msg.getInput(alloc, Params);
std.debug.assert(msg.sessionID != null); std.debug.assert(input.sessionId != null);
log.debug("Req > id {d}, method {s}", .{ msg.id, "page.navigate" }); log.debug("Req > id {d}, method {s}", .{ input.id, "page.navigate" });
const params = msg.params.?;
// change state // change state
ctx.state.url = params.url; ctx.state.url = input.params.url;
// TODO: hard coded ID // TODO: hard coded ID
ctx.state.loaderID = "AF8667A203C5392DBE9AC290044AA4C2"; ctx.state.loaderID = "AF8667A203C5392DBE9AC290044AA4C2";
@@ -289,7 +274,7 @@ fn navigate(
"Page.frameStartedLoading", "Page.frameStartedLoading",
FrameStartedLoading, FrameStartedLoading,
frame_started_loading, frame_started_loading,
msg.sessionID, input.sessionId,
); );
if (ctx.state.page_life_cycle_events) { if (ctx.state.page_life_cycle_events) {
life_event.name = "init"; life_event.name = "init";
@@ -300,7 +285,7 @@ fn navigate(
"Page.lifecycleEvent", "Page.lifecycleEvent",
LifecycleEvent, LifecycleEvent,
life_event, life_event,
msg.sessionID, input.sessionId,
); );
} }
@@ -330,14 +315,14 @@ fn navigate(
.frameId = ctx.state.frameID, .frameId = ctx.state.frameID,
.loaderId = ctx.state.loaderID, .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); try server.sendAsync(ctx, res);
// TODO: at this point do we need async the following actions to be async? // TODO: at this point do we need async the following actions to be async?
// Send Runtime.executionContextsCleared event // Send Runtime.executionContextsCleared event
// TODO: noop event, we have no env context at this point, is it necesarry? // 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 // Launch navigate
const p = try ctx.browser.session.createPage(); const p = try ctx.browser.session.createPage();
@@ -349,7 +334,7 @@ fn navigate(
.{ctx.state.frameID}, .{ctx.state.frameID},
); );
defer alloc.free(auxData); defer alloc.free(auxData);
try p.navigate(params.url, auxData); try p.navigate(input.params.url, auxData);
// Events // Events
@@ -364,7 +349,7 @@ fn navigate(
"Page.lifecycleEvent", "Page.lifecycleEvent",
LifecycleEvent, LifecycleEvent,
life_event, life_event,
msg.sessionID, input.sessionId,
); );
} }
@@ -388,7 +373,7 @@ fn navigate(
"Page.frameNavigated", "Page.frameNavigated",
FrameNavigated, FrameNavigated,
frame_navigated, frame_navigated,
msg.sessionID, input.sessionId,
); );
// domContentEventFired event // domContentEventFired event
@@ -400,7 +385,7 @@ fn navigate(
"Page.domContentEventFired", "Page.domContentEventFired",
cdp.TimestampEvent, cdp.TimestampEvent,
ts_event, ts_event,
msg.sessionID, input.sessionId,
); );
// lifecycle DOMContentLoaded event // lifecycle DOMContentLoaded event
@@ -414,7 +399,7 @@ fn navigate(
"Page.lifecycleEvent", "Page.lifecycleEvent",
LifecycleEvent, LifecycleEvent,
life_event, life_event,
msg.sessionID, input.sessionId,
); );
} }
@@ -427,7 +412,7 @@ fn navigate(
"Page.loadEventFired", "Page.loadEventFired",
cdp.TimestampEvent, cdp.TimestampEvent,
ts_event, ts_event,
msg.sessionID, input.sessionId,
); );
// lifecycle DOMContentLoaded event // lifecycle DOMContentLoaded event
@@ -441,7 +426,7 @@ fn navigate(
"Page.lifecycleEvent", "Page.lifecycleEvent",
LifecycleEvent, LifecycleEvent,
life_event, life_event,
msg.sessionID, input.sessionId,
); );
} }
@@ -453,7 +438,7 @@ fn navigate(
"Page.frameStoppedLoading", "Page.frameStoppedLoading",
FrameStoppedLoading, FrameStoppedLoading,
.{ .frameId = ctx.state.frameID }, .{ .frameId = ctx.state.frameID },
msg.sessionID, input.sessionId,
); );
return ""; return "";

View File

@@ -22,7 +22,7 @@ const server = @import("../server.zig");
const Ctx = server.Ctx; const Ctx = server.Ctx;
const cdp = @import("cdp.zig"); const cdp = @import("cdp.zig");
const result = cdp.result; const result = cdp.result;
const getMsg = cdp.getMsg; const IncomingMessage = @import("msg.zig").IncomingMessage;
const log = std.log.scoped(.cdp); const log = std.log.scoped(.cdp);
@@ -32,29 +32,26 @@ const Methods = enum {
pub fn performance( pub fn performance(
alloc: std.mem.Allocator, alloc: std.mem.Allocator,
id: ?u16, msg: *IncomingMessage,
action: []const u8, action: []const u8,
scanner: *std.json.Scanner,
ctx: *Ctx, ctx: *Ctx,
) ![]const u8 { ) ![]const u8 {
const method = std.meta.stringToEnum(Methods, action) orelse const method = std.meta.stringToEnum(Methods, action) orelse
return error.UnknownMethod; return error.UnknownMethod;
return switch (method) { return switch (method) {
.enable => enable(alloc, id, scanner, ctx), .enable => enable(alloc, msg, ctx),
}; };
} }
fn enable( fn enable(
alloc: std.mem.Allocator, alloc: std.mem.Allocator,
_id: ?u16, msg: *IncomingMessage,
scanner: *std.json.Scanner,
_: *Ctx, _: *Ctx,
) ![]const u8 { ) ![]const u8 {
// input // input
const msg = try getMsg(alloc, _id, void, scanner); const input = try msg.getInput(alloc, void);
log.debug("Req > id {d}, method {s}", .{ msg.id, "performance.enable" }); 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);
} }

View File

@@ -25,7 +25,7 @@ const server = @import("../server.zig");
const Ctx = server.Ctx; const Ctx = server.Ctx;
const cdp = @import("cdp.zig"); const cdp = @import("cdp.zig");
const result = cdp.result; const result = cdp.result;
const getMsg = cdp.getMsg; const IncomingMessage = @import("msg.zig").IncomingMessage;
const stringify = cdp.stringify; const stringify = cdp.stringify;
const log = std.log.scoped(.cdp); const log = std.log.scoped(.cdp);
@@ -41,27 +41,23 @@ const Methods = enum {
pub fn runtime( pub fn runtime(
alloc: std.mem.Allocator, alloc: std.mem.Allocator,
id: ?u16, msg: *IncomingMessage,
action: []const u8, action: []const u8,
scanner: *std.json.Scanner,
s: []const u8,
ctx: *Ctx, ctx: *Ctx,
) ![]const u8 { ) ![]const u8 {
const method = std.meta.stringToEnum(Methods, action) orelse 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 // NOTE: we could send it anyway to the JS runtime but it's good to check it
return error.UnknownMethod; return error.UnknownMethod;
return switch (method) { return switch (method) {
.runIfWaitingForDebugger => runIfWaitingForDebugger(alloc, id, scanner, ctx), .runIfWaitingForDebugger => runIfWaitingForDebugger(alloc, msg, ctx),
else => sendInspector(alloc, method, id, s, scanner, ctx), else => sendInspector(alloc, method, msg, ctx),
}; };
} }
fn sendInspector( fn sendInspector(
alloc: std.mem.Allocator, alloc: std.mem.Allocator,
method: Methods, method: Methods,
_id: ?u16, msg: *IncomingMessage,
s: []const u8,
scanner: *std.json.Scanner,
ctx: *Ctx, ctx: *Ctx,
) ![]const u8 { ) ![]const u8 {
@@ -69,8 +65,8 @@ fn sendInspector(
if (std.log.defaultLogEnabled(.debug)) { if (std.log.defaultLogEnabled(.debug)) {
// input // input
var script: ?[]const u8 = null;
var id: u16 = undefined; var id: u16 = undefined;
var script: ?[]const u8 = null;
if (method == .evaluate) { if (method == .evaluate) {
const Params = struct { const Params = struct {
@@ -81,11 +77,10 @@ fn sendInspector(
userGesture: ?bool = null, userGesture: ?bool = null,
}; };
const msg = try getMsg(alloc, _id, Params, scanner); const input = try msg.getInput(alloc, Params);
log.debug("Req > id {d}, method {s} (script saved on cache)", .{ msg.id, "runtime.evaluate" }); log.debug("Req > id {d}, method {s} (script saved on cache)", .{ input.id, "runtime.evaluate" });
const params = msg.params.?; script = input.params.expression;
script = params.expression; id = input.id;
id = msg.id;
} else if (method == .callFunctionOn) { } else if (method == .callFunctionOn) {
const Params = struct { const Params = struct {
functionDeclaration: []const u8, functionDeclaration: []const u8,
@@ -100,11 +95,10 @@ fn sendInspector(
userGesture: ?bool = null, userGesture: ?bool = null,
}; };
const msg = try getMsg(alloc, _id, Params, scanner); const input = try msg.getInput(alloc, Params);
log.debug("Req > id {d}, method {s} (script saved on cache)", .{ msg.id, "runtime.callFunctionOn" }); log.debug("Req > id {d}, method {s} (script saved on cache)", .{ input.id, "runtime.callFunctionOn" });
const params = msg.params.?; script = input.params.functionDeclaration;
script = params.functionDeclaration; id = input.id;
id = msg.id;
} }
if (script) |src| { if (script) |src| {
@@ -115,12 +109,12 @@ fn sendInspector(
// remove awaitPromise true params // remove awaitPromise true params
// TODO: delete when Promise are correctly handled by zig-js-runtime // TODO: delete when Promise are correctly handled by zig-js-runtime
if (method == .callFunctionOn or method == .evaluate) { 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); 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); ctx.sendInspector(buf);
} else { } else {
ctx.sendInspector(s); ctx.sendInspector(msg.json);
} }
return ""; return "";
} }
@@ -166,14 +160,11 @@ pub fn executionContextCreated(
// should we be passing this also to the JS Inspector? // should we be passing this also to the JS Inspector?
fn runIfWaitingForDebugger( fn runIfWaitingForDebugger(
alloc: std.mem.Allocator, alloc: std.mem.Allocator,
_id: ?u16, msg: *IncomingMessage,
scanner: *std.json.Scanner,
_: *Ctx, _: *Ctx,
) ![]const u8 { ) ![]const u8 {
const input = try msg.getInput(alloc, void);
log.debug("Req > id {d}, method {s}", .{ input.id, "runtime.runIfWaitingForDebugger" });
// input return result(alloc, input.id, null, null, input.sessionId);
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);
} }

View File

@@ -22,8 +22,8 @@ const server = @import("../server.zig");
const Ctx = server.Ctx; const Ctx = server.Ctx;
const cdp = @import("cdp.zig"); const cdp = @import("cdp.zig");
const result = cdp.result; const result = cdp.result;
const getMsg = cdp.getMsg;
const stringify = cdp.stringify; const stringify = cdp.stringify;
const IncomingMessage = @import("msg.zig").IncomingMessage;
const log = std.log.scoped(.cdp); const log = std.log.scoped(.cdp);
@@ -41,23 +41,22 @@ const Methods = enum {
pub fn target( pub fn target(
alloc: std.mem.Allocator, alloc: std.mem.Allocator,
id: ?u16, msg: *IncomingMessage,
action: []const u8, action: []const u8,
scanner: *std.json.Scanner,
ctx: *Ctx, ctx: *Ctx,
) ![]const u8 { ) ![]const u8 {
const method = std.meta.stringToEnum(Methods, action) orelse const method = std.meta.stringToEnum(Methods, action) orelse
return error.UnknownMethod; return error.UnknownMethod;
return switch (method) { return switch (method) {
.setDiscoverTargets => setDiscoverTargets(alloc, id, scanner, ctx), .setDiscoverTargets => setDiscoverTargets(alloc, msg, ctx),
.setAutoAttach => setAutoAttach(alloc, id, scanner, ctx), .setAutoAttach => setAutoAttach(alloc, msg, ctx),
.attachToTarget => attachToTarget(alloc, id, scanner, ctx), .attachToTarget => attachToTarget(alloc, msg, ctx),
.getTargetInfo => getTargetInfo(alloc, id, scanner, ctx), .getTargetInfo => getTargetInfo(alloc, msg, ctx),
.getBrowserContexts => getBrowserContexts(alloc, id, scanner, ctx), .getBrowserContexts => getBrowserContexts(alloc, msg, ctx),
.createBrowserContext => createBrowserContext(alloc, id, scanner, ctx), .createBrowserContext => createBrowserContext(alloc, msg, ctx),
.disposeBrowserContext => disposeBrowserContext(alloc, id, scanner, ctx), .disposeBrowserContext => disposeBrowserContext(alloc, msg, ctx),
.createTarget => createTarget(alloc, id, scanner, ctx), .createTarget => createTarget(alloc, msg, ctx),
.closeTarget => closeTarget(alloc, id, scanner, ctx), .closeTarget => closeTarget(alloc, msg, ctx),
}; };
} }
@@ -69,17 +68,15 @@ const BrowserContextID = "65618675CB7D3585A95049E9DFE95EA9";
// TODO: noop method // TODO: noop method
fn setDiscoverTargets( fn setDiscoverTargets(
alloc: std.mem.Allocator, alloc: std.mem.Allocator,
_id: ?u16, msg: *IncomingMessage,
scanner: *std.json.Scanner,
_: *Ctx, _: *Ctx,
) ![]const u8 { ) ![]const u8 {
// input // input
const msg = try getMsg(alloc, _id, void, scanner); const input = try msg.getInput(alloc, void);
log.debug("Req > id {d}, method {s}", .{ msg.id, "target.setDiscoverTargets" }); log.debug("Req > id {d}, method {s}", .{ input.id, "target.setDiscoverTargets" });
// output // output
return result(alloc, msg.id, null, null, msg.sessionID); return result(alloc, input.id, null, null, input.sessionId);
} }
const AttachToTarget = struct { const AttachToTarget = struct {
@@ -104,11 +101,9 @@ const TargetFilter = struct {
// TODO: noop method // TODO: noop method
fn setAutoAttach( fn setAutoAttach(
alloc: std.mem.Allocator, alloc: std.mem.Allocator,
_id: ?u16, msg: *IncomingMessage,
scanner: *std.json.Scanner,
ctx: *Ctx, ctx: *Ctx,
) ![]const u8 { ) ![]const u8 {
// input // input
const Params = struct { const Params = struct {
autoAttach: bool, autoAttach: bool,
@@ -116,11 +111,11 @@ fn setAutoAttach(
flatten: bool = true, flatten: bool = true,
filter: ?[]TargetFilter = null, filter: ?[]TargetFilter = null,
}; };
const msg = try getMsg(alloc, _id, Params, scanner); const input = try msg.getInput(alloc, Params);
log.debug("Req > id {d}, method {s}", .{ msg.id, "target.setAutoAttach" }); log.debug("Req > id {d}, method {s}", .{ input.id, "target.setAutoAttach" });
// attachedToTarget event // attachedToTarget event
if (msg.sessionID == null) { if (input.sessionId == null) {
const attached = AttachToTarget{ const attached = AttachToTarget{
.sessionId = cdp.BrowserSessionID, .sessionId = cdp.BrowserSessionID,
.targetInfo = .{ .targetInfo = .{
@@ -134,14 +129,13 @@ fn setAutoAttach(
} }
// output // output
return result(alloc, msg.id, null, null, msg.sessionID); return result(alloc, input.id, null, null, input.sessionId);
} }
// TODO: noop method // TODO: noop method
fn attachToTarget( fn attachToTarget(
alloc: std.mem.Allocator, alloc: std.mem.Allocator,
_id: ?u16, msg: *IncomingMessage,
scanner: *std.json.Scanner,
ctx: *Ctx, ctx: *Ctx,
) ![]const u8 { ) ![]const u8 {
@@ -150,11 +144,11 @@ fn attachToTarget(
targetId: []const u8, targetId: []const u8,
flatten: bool = true, flatten: bool = true,
}; };
const msg = try getMsg(alloc, _id, Params, scanner); const input = try msg.getInput(alloc, Params);
log.debug("Req > id {d}, method {s}", .{ msg.id, "target.setAutoAttach" }); log.debug("Req > id {d}, method {s}", .{ input.id, "target.attachToTarget" });
// attachedToTarget event // attachedToTarget event
if (msg.sessionID == null) { if (input.sessionId == null) {
const attached = AttachToTarget{ const attached = AttachToTarget{
.sessionId = cdp.BrowserSessionID, .sessionId = cdp.BrowserSessionID,
.targetInfo = .{ .targetInfo = .{
@@ -172,24 +166,22 @@ fn attachToTarget(
sessionId: []const u8, sessionId: []const u8,
}; };
const output = SessionId{ 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( fn getTargetInfo(
alloc: std.mem.Allocator, alloc: std.mem.Allocator,
_id: ?u16, msg: *IncomingMessage,
scanner: *std.json.Scanner,
_: *Ctx, _: *Ctx,
) ![]const u8 { ) ![]const u8 {
// input // input
const Params = struct { const Params = struct {
targetId: ?[]const u8 = null, targetId: ?[]const u8 = null,
}; };
const msg = try getMsg(alloc, _id, Params, scanner); const input = try msg.getInput(alloc, Params);
log.debug("Req > id {d}, method {s}", .{ msg.id, "target.getTargetInfo" }); log.debug("Req > id {d}, method {s}", .{ input.id, "target.getTargetInfo" });
// output // output
const TargetInfo = struct { const TargetInfo = struct {
@@ -208,7 +200,7 @@ fn getTargetInfo(
.targetId = BrowserTargetID, .targetId = BrowserTargetID,
.type = "browser", .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 // Browser context are not handled and not in the roadmap for now
@@ -217,14 +209,12 @@ fn getTargetInfo(
// TODO: noop method // TODO: noop method
fn getBrowserContexts( fn getBrowserContexts(
alloc: std.mem.Allocator, alloc: std.mem.Allocator,
_id: ?u16, msg: *IncomingMessage,
scanner: *std.json.Scanner,
ctx: *Ctx, ctx: *Ctx,
) ![]const u8 { ) ![]const u8 {
// input // input
const msg = try getMsg(alloc, _id, void, scanner); const input = try msg.getInput(alloc, void);
log.debug("Req > id {d}, method {s}", .{ msg.id, "target.getBrowserContexts" }); log.debug("Req > id {d}, method {s}", .{ input.id, "target.getBrowserContexts" });
// ouptut // ouptut
const Resp = struct { const Resp = struct {
@@ -238,7 +228,7 @@ fn getBrowserContexts(
const contextIDs = [0][]const u8{}; const contextIDs = [0][]const u8{};
resp = .{ .browserContextIds = &contextIDs }; resp = .{ .browserContextIds = &contextIDs };
} }
return result(alloc, msg.id, Resp, resp, null); return result(alloc, input.id, Resp, resp, null);
} }
const ContextID = "22648B09EDCCDD11109E2D4FEFBE4F89"; const ContextID = "22648B09EDCCDD11109E2D4FEFBE4F89";
@@ -246,11 +236,9 @@ const ContextID = "22648B09EDCCDD11109E2D4FEFBE4F89";
// TODO: noop method // TODO: noop method
fn createBrowserContext( fn createBrowserContext(
alloc: std.mem.Allocator, alloc: std.mem.Allocator,
_id: ?u16, msg: *IncomingMessage,
scanner: *std.json.Scanner,
ctx: *Ctx, ctx: *Ctx,
) ![]const u8 { ) ![]const u8 {
// input // input
const Params = struct { const Params = struct {
disposeOnDetach: bool = false, disposeOnDetach: bool = false,
@@ -258,8 +246,8 @@ fn createBrowserContext(
proxyBypassList: ?[]const u8 = null, proxyBypassList: ?[]const u8 = null,
originsWithUniversalNetworkAccess: ?[][]const u8 = null, originsWithUniversalNetworkAccess: ?[][]const u8 = null,
}; };
const msg = try getMsg(alloc, _id, Params, scanner); const input = try msg.getInput(alloc, Params);
log.debug("Req > id {d}, method {s}", .{ msg.id, "target.createBrowserContext" }); log.debug("Req > id {d}, method {s}", .{ input.id, "target.createBrowserContext" });
ctx.state.contextID = ContextID; ctx.state.contextID = ContextID;
@@ -279,25 +267,23 @@ fn createBrowserContext(
try writer.writeAll(" }"); try writer.writeAll(" }");
} }
}; };
return result(alloc, msg.id, Resp, Resp{}, msg.sessionID); return result(alloc, input.id, Resp, Resp{}, input.sessionId);
} }
fn disposeBrowserContext( fn disposeBrowserContext(
alloc: std.mem.Allocator, alloc: std.mem.Allocator,
_id: ?u16, msg: *IncomingMessage,
scanner: *std.json.Scanner,
ctx: *Ctx, ctx: *Ctx,
) ![]const u8 { ) ![]const u8 {
// input // input
const Params = struct { const Params = struct {
browserContextId: []const u8, browserContextId: []const u8,
}; };
const msg = try getMsg(alloc, _id, Params, scanner); const input = try msg.getInput(alloc, Params);
log.debug("Req > id {d}, method {s}", .{ msg.id, "target.disposeBrowserContext" }); log.debug("Req > id {d}, method {s}", .{ input.id, "target.disposeBrowserContext" });
// output // output
const res = try result(alloc, msg.id, null, .{}, null); const res = try result(alloc, input.id, null, .{}, null);
try server.sendAsync(ctx, res); try server.sendAsync(ctx, res);
return error.DisposeBrowserContext; return error.DisposeBrowserContext;
@@ -309,11 +295,9 @@ const LoaderID = "DD4A76F842AA389647D702B4D805F49A";
fn createTarget( fn createTarget(
alloc: std.mem.Allocator, alloc: std.mem.Allocator,
_id: ?u16, msg: *IncomingMessage,
scanner: *std.json.Scanner,
ctx: *Ctx, ctx: *Ctx,
) ![]const u8 { ) ![]const u8 {
// input // input
const Params = struct { const Params = struct {
url: []const u8, url: []const u8,
@@ -325,8 +309,8 @@ fn createTarget(
background: bool = false, background: bool = false,
forTab: ?bool = null, forTab: ?bool = null,
}; };
const msg = try getMsg(alloc, _id, Params, scanner); const input = try msg.getInput(alloc, Params);
log.debug("Req > id {d}, method {s}", .{ msg.id, "target.createTarget" }); log.debug("Req > id {d}, method {s}", .{ input.id, "target.createTarget" });
// change CDP state // change CDP state
ctx.state.frameID = TargetID; ctx.state.frameID = TargetID;
@@ -346,7 +330,7 @@ fn createTarget(
}, },
.waitingForDebugger = true, .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 // output
const Resp = struct { const Resp = struct {
@@ -364,28 +348,26 @@ fn createTarget(
try writer.writeAll(" }"); try writer.writeAll(" }");
} }
}; };
return result(alloc, msg.id, Resp, Resp{}, msg.sessionID); return result(alloc, input.id, Resp, Resp{}, input.sessionId);
} }
fn closeTarget( fn closeTarget(
alloc: std.mem.Allocator, alloc: std.mem.Allocator,
_id: ?u16, msg: *IncomingMessage,
scanner: *std.json.Scanner,
ctx: *Ctx, ctx: *Ctx,
) ![]const u8 { ) ![]const u8 {
// input // input
const Params = struct { const Params = struct {
targetId: []const u8, targetId: []const u8,
}; };
const msg = try getMsg(alloc, _id, Params, scanner); const input = try msg.getInput(alloc, Params);
log.debug("Req > id {d}, method {s}", .{ msg.id, "target.closeTarget" }); log.debug("Req > id {d}, method {s}", .{ input.id, "target.closeTarget" });
// output // output
const Resp = struct { const Resp = struct {
success: bool = true, 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); try server.sendAsync(ctx, res);
// Inspector.detached event // Inspector.detached event
@@ -398,7 +380,7 @@ fn closeTarget(
"Inspector.detached", "Inspector.detached",
InspectorDetached, InspectorDetached,
.{}, .{},
msg.sessionID orelse cdp.ContextSessionID, input.sessionId orelse cdp.ContextSessionID,
); );
// detachedFromTarget event // detachedFromTarget event
@@ -412,8 +394,8 @@ fn closeTarget(
"Target.detachedFromTarget", "Target.detachedFromTarget",
TargetDetached, TargetDetached,
.{ .{
.sessionId = msg.sessionID orelse cdp.ContextSessionID, .sessionId = input.sessionId orelse cdp.ContextSessionID,
.targetId = msg.params.?.targetId, .targetId = input.params.targetId,
}, },
null, null,
); );

View File

@@ -321,6 +321,8 @@ test {
const queryTest = @import("url/query.zig"); const queryTest = @import("url/query.zig");
std.testing.refAllDecls(queryTest); std.testing.refAllDecls(queryTest);
std.testing.refAllDecls(@import("cdp/msg.zig"));
} }
fn testJSRuntime(alloc: std.mem.Allocator) !void { fn testJSRuntime(alloc: std.mem.Allocator) !void {