Redirect Runtime domain to JS engine Inspector

Signed-off-by: Francis Bouvier <francis@lightpanda.io>
This commit is contained in:
Francis Bouvier
2024-10-01 17:12:08 +02:00
parent 14a3a662fd
commit 94d2d28806
8 changed files with 195 additions and 254 deletions

View File

@@ -51,10 +51,9 @@ const log = std.log.scoped(.browser);
pub const Browser = struct { pub const Browser = struct {
session: *Session, session: *Session,
pub fn init(alloc: std.mem.Allocator, vm: jsruntime.VM) !Browser { pub fn init(alloc: std.mem.Allocator) !Browser {
// We want to ensure the caller initialised a VM, but the browser // We want to ensure the caller initialised a VM, but the browser
// doesn't use it directly... // doesn't use it directly...
_ = vm;
return Browser{ return Browser{
.session = try Session.init(alloc, "about:blank"), .session = try Session.init(alloc, "about:blank"),
@@ -91,6 +90,7 @@ pub const Session = struct {
loader: Loader, loader: Loader,
env: Env = undefined, env: Env = undefined,
loop: Loop, loop: Loop,
inspector: ?jsruntime.Inspector = null,
window: Window, window: Window,
// TODO move the shed to the browser? // TODO move the shed to the browser?
storageShed: storage.Shed, storageShed: storage.Shed,
@@ -122,6 +122,10 @@ pub const Session = struct {
fn deinit(self: *Session) void { fn deinit(self: *Session) void {
if (self.page) |page| page.end(); if (self.page) |page| page.end();
if (self.inspector) |inspector| {
inspector.deinit(self.alloc);
}
self.env.deinit(); self.env.deinit();
self.arena.deinit(); self.arena.deinit();
@@ -132,9 +136,25 @@ pub const Session = struct {
self.alloc.destroy(self); self.alloc.destroy(self);
} }
pub fn setInspector(
self: *Session,
ctx: *anyopaque,
onResp: jsruntime.InspectorOnResponseFn,
onEvent: jsruntime.InspectorOnEventFn,
) !void {
self.inspector = try jsruntime.Inspector.init(self.alloc, self.env, ctx, onResp, onEvent);
self.env.setInspector(self.inspector.?);
}
pub fn createPage(self: *Session) !Page { pub fn createPage(self: *Session) !Page {
return Page.init(self.alloc, self); return Page.init(self.alloc, self);
} }
pub fn callInspector(self: *Session, msg: []const u8) void {
if (self.inspector) |inspector| {
inspector.send(msg, self.env);
}
}
}; };
// Page navigates to an url. // Page navigates to an url.
@@ -219,7 +239,7 @@ pub const Page = struct {
} }
// spec reference: https://html.spec.whatwg.org/#document-lifecycle // spec reference: https://html.spec.whatwg.org/#document-lifecycle
pub fn navigate(self: *Page, uri: []const u8) !void { pub fn navigate(self: *Page, uri: []const u8, auxData: ?[]const u8) !void {
const alloc = self.arena.allocator(); const alloc = self.arena.allocator();
log.debug("starting GET {s}", .{uri}); log.debug("starting GET {s}", .{uri});
@@ -280,7 +300,7 @@ pub const Page = struct {
log.debug("header content-type: {s}", .{ct.?}); log.debug("header content-type: {s}", .{ct.?});
const mime = try Mime.parse(ct.?); const mime = try Mime.parse(ct.?);
if (mime.eql(Mime.HTML)) { if (mime.eql(Mime.HTML)) {
try self.loadHTMLDoc(req.reader(), mime.charset orelse "utf-8"); try self.loadHTMLDoc(req.reader(), mime.charset orelse "utf-8", auxData);
} else { } else {
log.info("non-HTML document: {s}", .{ct.?}); log.info("non-HTML document: {s}", .{ct.?});
@@ -290,7 +310,7 @@ pub const Page = struct {
} }
// https://html.spec.whatwg.org/#read-html // https://html.spec.whatwg.org/#read-html
fn loadHTMLDoc(self: *Page, reader: anytype, charset: []const u8) !void { fn loadHTMLDoc(self: *Page, reader: anytype, charset: []const u8, auxData: ?[]const u8) !void {
const alloc = self.arena.allocator(); const alloc = self.arena.allocator();
// start netsurf memory arena. // start netsurf memory arena.
@@ -327,6 +347,11 @@ pub const Page = struct {
log.debug("start js env", .{}); log.debug("start js env", .{});
try self.session.env.start(); try self.session.env.start();
// inspector
if (self.session.inspector) |inspector| {
inspector.contextCreated(self.session.env, "", self.origin.?, auxData);
}
// replace the user context document with the new one. // replace the user context document with the new one.
try self.session.env.setUserContext(.{ try self.session.env.setUserContext(.{
.document = html_doc, .document = html_doc,

View File

@@ -16,6 +16,7 @@ const performance = @import("performance.zig").performance;
pub const Error = error{ pub const Error = error{
UnknonwDomain, UnknonwDomain,
UnknownMethod, UnknownMethod,
NoResponse,
}; };
pub fn isCdpError(err: anyerror) ?Error { pub fn isCdpError(err: anyerror) ?Error {
@@ -85,7 +86,7 @@ pub fn do(
.Target => target(alloc, id, iter.next().?, &scanner, ctx), .Target => target(alloc, id, iter.next().?, &scanner, ctx),
.Page => page(alloc, id, iter.next().?, &scanner, ctx), .Page => page(alloc, id, iter.next().?, &scanner, ctx),
.Log => log(alloc, id, iter.next().?, &scanner, ctx), .Log => log(alloc, id, iter.next().?, &scanner, ctx),
.Runtime => runtime(alloc, id, iter.next().?, &scanner, ctx), .Runtime => runtime(alloc, id, iter.next().?, &scanner, s, ctx),
.Network => network(alloc, id, iter.next().?, &scanner, ctx), .Network => network(alloc, id, iter.next().?, &scanner, ctx),
.Emulation => emulation(alloc, id, iter.next().?, &scanner, ctx), .Emulation => emulation(alloc, id, iter.next().?, &scanner, ctx),
.Fetch => fetch(alloc, id, iter.next().?, &scanner, ctx), .Fetch => fetch(alloc, id, iter.next().?, &scanner, ctx),
@@ -199,7 +200,7 @@ fn getParams(
key: []const u8, key: []const u8,
) !?T { ) !?T {
// check key key is "params" // check key is "params"
if (!std.mem.eql(u8, "params", key)) return null; if (!std.mem.eql(u8, "params", key)) return null;
// skip "params" if not requested // skip "params" if not requested
@@ -285,7 +286,8 @@ pub fn getMsg(
// Common // Common
// ------ // ------
pub const SessionID = "9559320D92474062597D9875C664CAC0"; pub const BrowserSessionID = "9559320D92474062597D9875C664CAC0";
pub const ContextSessionID = "4FDC2CB760A23A220497A05C95417CF4";
pub const URLBase = "chrome://newtab/"; pub const URLBase = "chrome://newtab/";
pub const FrameID = "90D14BBD8AED408A0467AC93100BCDBE"; pub const FrameID = "90D14BBD8AED408A0467AC93100BCDBE";
pub const LoaderID = "CFC8BED824DD2FD56CF1EF33C965C79C"; pub const LoaderID = "CFC8BED824DD2FD56CF1EF33C965C79C";

View File

@@ -144,13 +144,38 @@ fn createIsolatedWorld(
alloc: std.mem.Allocator, alloc: std.mem.Allocator,
id: ?u16, id: ?u16,
scanner: *std.json.Scanner, scanner: *std.json.Scanner,
_: *Ctx, ctx: *Ctx,
) ![]const u8 { ) ![]const u8 {
const msg = try getMsg(alloc, void, scanner);
// input
const Params = struct {
frameId: []const u8,
worldName: []const u8,
grantUniveralAccess: bool,
};
const msg = try getMsg(alloc, Params, scanner);
std.debug.assert(msg.sessionID != null);
const params = msg.params.?;
// noop executionContextCreated event
try Runtime.executionContextCreated(
alloc,
ctx,
0,
"",
params.worldName,
"7102379147004877974.3265385113993241162",
.{
.isDefault = false,
.type = "isolated",
.frameId = params.frameId,
},
msg.sessionID,
);
// output // output
const Resp = struct { const Resp = struct {
executionContextId: u8 = 2, executionContextId: u8 = 0,
}; };
return result(alloc, id orelse msg.id.?, Resp, .{}, msg.sessionID); return result(alloc, id orelse msg.id.?, Resp, .{}, msg.sessionID);
@@ -230,20 +255,14 @@ fn navigate(
// Launch navigate // Launch navigate
var p = try ctx.browser.currentSession().createPage(); var p = try ctx.browser.currentSession().createPage();
_ = try p.navigate(params.url);
// Send create runtime context event
ctx.state.executionContextId += 1; ctx.state.executionContextId += 1;
try Runtime.executionContextCreated( const auxData = try std.fmt.allocPrint(
alloc, alloc,
ctx, "{{\"isDefault\":true,\"type\":\"default\",\"frameId\":\"{s}\"}}",
ctx.state.executionContextId, .{ctx.state.frameID},
"http://127.0.0.1:1234", // TODO: real domain
"",
"7102379147004877974.3265385113993241162",
.{ .frameId = ctx.state.frameID },
msg.sessionID,
); );
defer alloc.free(auxData);
_ = try p.navigate(params.url, auxData);
// frameNavigated event // frameNavigated event
const FrameNavigated = struct { const FrameNavigated = struct {

View File

@@ -16,6 +16,7 @@ const Methods = enum {
evaluate, evaluate,
addBinding, addBinding,
callFunctionOn, callFunctionOn,
releaseObject,
}; };
pub fn runtime( pub fn runtime(
@@ -23,44 +24,83 @@ pub fn runtime(
id: ?u16, id: ?u16,
action: []const u8, action: []const u8,
scanner: *std.json.Scanner, 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
return error.UnknownMethod; return error.UnknownMethod;
return switch (method) { return switch (method) {
.enable => enable(alloc, id, scanner, ctx),
.runIfWaitingForDebugger => runIfWaitingForDebugger(alloc, id, scanner, ctx), .runIfWaitingForDebugger => runIfWaitingForDebugger(alloc, id, scanner, ctx),
.evaluate => evaluate(alloc, id, scanner, ctx), else => sendInspector(alloc, method, id, s, scanner, ctx),
.addBinding => addBinding(alloc, id, scanner, ctx),
.callFunctionOn => callFunctionOn(alloc, id, scanner, ctx),
}; };
} }
fn enable( fn sendInspector(
alloc: std.mem.Allocator, alloc: std.mem.Allocator,
id: ?u16, method: Methods,
_id: ?u16,
s: []const u8,
scanner: *std.json.Scanner, scanner: *std.json.Scanner,
_: *Ctx, ctx: *Ctx,
) ![]const u8 { ) ![]const u8 {
// save script in file at debug mode
if (std.log.defaultLogEnabled(.debug)) {
// input // input
const msg = try getMsg(alloc, void, scanner); var script: ?[]const u8 = null;
var id: u16 = undefined;
// output if (method == .evaluate) {
// const uniqueID = "1367118932354479079.-1471398151593995849"; const Params = struct {
// const mainCtx = try executionContextCreated( expression: []const u8,
// alloc, contextId: ?u8 = null,
// 1, returnByValue: ?bool = null,
// cdp.URLBase, awaitPromise: ?bool = null,
// "", userGesture: ?bool = null,
// uniqueID, };
// .{},
// sessionID,
// );
// std.log.debug("res {s}", .{mainCtx});
// try server.sendAsync(ctx, mainCtx);
return result(alloc, id orelse msg.id.?, null, null, msg.sessionID); const msg = try getMsg(alloc, Params, scanner);
const params = msg.params.?;
script = params.expression;
id = _id orelse msg.id.?;
} else if (method == .callFunctionOn) {
const Params = struct {
functionDeclaration: []const u8,
objectId: ?[]const u8 = null,
executionContextId: ?u8 = null,
arguments: ?[]struct {
value: ?[]const u8 = null,
objectId: ?[]const u8 = null,
} = null,
returnByValue: ?bool = null,
awaitPromise: ?bool = null,
userGesture: ?bool = null,
};
const msg = try getMsg(alloc, Params, scanner);
const params = msg.params.?;
script = params.functionDeclaration;
id = _id orelse msg.id.?;
}
if (script) |src| {
try cdp.dumpFile(alloc, id, src);
}
}
// 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);
defer alloc.free(buf);
_ = std.mem.replace(u8, s, "\"awaitPromise\":true", "\"awaitPromise\":false", buf);
ctx.sendInspector(buf);
} else {
ctx.sendInspector(s);
}
return "";
} }
pub const AuxData = struct { pub const AuxData = struct {
@@ -69,14 +109,6 @@ pub const AuxData = struct {
frameId: []const u8 = cdp.FrameID, frameId: []const u8 = cdp.FrameID,
}; };
const ExecutionContextDescription = struct {
id: u64,
origin: []const u8,
name: []const u8,
uniqueId: []const u8,
auxData: ?AuxData = null,
};
pub fn executionContextCreated( pub fn executionContextCreated(
alloc: std.mem.Allocator, alloc: std.mem.Allocator,
ctx: *Ctx, ctx: *Ctx,
@@ -88,7 +120,13 @@ pub fn executionContextCreated(
sessionID: ?[]const u8, sessionID: ?[]const u8,
) !void { ) !void {
const Params = struct { const Params = struct {
context: ExecutionContextDescription, context: struct {
id: u64,
origin: []const u8,
name: []const u8,
uniqueId: []const u8,
auxData: ?AuxData = null,
},
}; };
const params = Params{ const params = Params{
.context = .{ .context = .{
@@ -112,195 +150,3 @@ fn runIfWaitingForDebugger(
return result(alloc, id orelse msg.id.?, null, null, msg.sessionID); return result(alloc, id orelse msg.id.?, null, null, msg.sessionID);
} }
fn evaluate(
alloc: std.mem.Allocator,
_id: ?u16,
scanner: *std.json.Scanner,
ctx: *Ctx,
) ![]const u8 {
// ensure a page has been previously created
if (ctx.browser.currentSession().page == null) return error.CDPNoPage;
// input
const Params = struct {
expression: []const u8,
contextId: ?u8 = null,
returnByValue: ?bool = null,
awaitPromise: ?bool = null,
userGesture: ?bool = null,
};
const msg = try getMsg(alloc, Params, scanner);
std.debug.assert(msg.sessionID != null);
const params = msg.params.?;
const id = _id orelse msg.id.?;
// save script in file at debug mode
std.log.debug("script {d} length: {d}", .{ id, params.expression.len });
if (std.log.defaultLogEnabled(.debug)) {
try cdp.dumpFile(alloc, id, params.expression);
}
// evaluate the script in the context of the current page
const session = ctx.browser.currentSession();
// TODO: should we use instead the allocator of the page?
// the following code does not work with session.page.?.arena.allocator() as alloc
const res = try runtimeEvaluate(session.alloc, id, session.env, params.expression, "cdp");
// check result
const res_type = try res.typeOf(session.env);
// TODO: Resp should depends on JS result returned by the JS engine
const Resp = struct {
result: struct {
type: []const u8,
subtype: ?[]const u8 = null,
className: ?[]const u8 = null,
description: ?[]const u8 = null,
objectId: ?[]const u8 = null,
},
};
var resp = Resp{
.result = .{
.type = @tagName(res_type),
},
};
if (res_type == .object) {
resp.result.className = "Object";
resp.result.description = "Object";
resp.result.objectId = "-9051357107442861868.3.2";
}
return result(alloc, id, Resp, resp, msg.sessionID);
}
fn addBinding(
alloc: std.mem.Allocator,
_id: ?u16,
scanner: *std.json.Scanner,
ctx: *Ctx,
) ![]const u8 {
// input
const Params = struct {
name: []const u8,
executionContextId: ?u8 = null,
};
const msg = try getMsg(alloc, Params, scanner);
const id = _id orelse msg.id.?;
const params = msg.params.?;
if (params.executionContextId) |contextId| {
std.debug.assert(contextId == ctx.state.executionContextId);
}
const script = try std.fmt.allocPrint(alloc, "globalThis['{s}'] = {{}};", .{params.name});
defer alloc.free(script);
const session = ctx.browser.currentSession();
_ = try runtimeEvaluate(session.alloc, id, session.env, script, "addBinding");
return result(alloc, id, null, null, msg.sessionID);
}
fn callFunctionOn(
alloc: std.mem.Allocator,
_id: ?u16,
scanner: *std.json.Scanner,
ctx: *Ctx,
) ![]const u8 {
// input
const Params = struct {
functionDeclaration: []const u8,
objectId: ?[]const u8 = null,
executionContextId: ?u8 = null,
arguments: ?[]struct {
value: ?[]const u8 = null,
} = null,
returnByValue: ?bool = null,
awaitPromise: ?bool = null,
userGesture: ?bool = null,
};
const msg = try getMsg(alloc, Params, scanner);
const id = _id orelse msg.id.?;
const params = msg.params.?;
std.debug.assert(params.objectId != null or params.executionContextId != null);
if (params.executionContextId) |contextID| {
std.debug.assert(contextID == ctx.state.executionContextId);
}
const name = "callFunctionOn";
// save script in file at debug mode
std.log.debug("{s} script id {d}, length: {d}", .{ name, id, params.functionDeclaration.len });
if (std.log.defaultLogEnabled(.debug)) {
try cdp.dumpFile(alloc, id, params.functionDeclaration);
}
// parse function
if (!std.mem.startsWith(u8, params.functionDeclaration, "function ")) {
return error.CDPRuntimeCallFunctionOnNotFunction;
}
const pos = std.mem.indexOfScalar(u8, params.functionDeclaration, '(');
if (pos == null) return error.CDPRuntimeCallFunctionOnWrongFunction;
var function = params.functionDeclaration[9..pos.?];
function = try std.fmt.allocPrint(alloc, "{s}(", .{function});
defer alloc.free(function);
if (params.arguments) |args| {
for (args, 0..) |arg, i| {
if (i > 0) {
function = try std.fmt.allocPrint(alloc, "{s}, ", .{function});
}
if (arg.value) |value| {
function = try std.fmt.allocPrint(alloc, "{s}\"{s}\"", .{ function, value });
} else {
function = try std.fmt.allocPrint(alloc, "{s}undefined", .{function});
}
}
}
function = try std.fmt.allocPrint(alloc, "{s});", .{function});
std.log.debug("{s} id {d}, function parsed: {s}", .{ name, id, function });
const session = ctx.browser.currentSession();
// TODO: should we use the page's allocator instead of the session's allocator?
// the following code does not work with session.page.?.arena.allocator() as alloc
// first evaluate the function declaration
_ = try runtimeEvaluate(session.alloc, id, session.env, params.functionDeclaration, name);
// then call the function on the arguments
_ = try runtimeEvaluate(session.alloc, id, session.env, function, name);
return result(alloc, id, null, "{\"type\":\"undefined\"}", msg.sessionID);
}
// caller is the owner of JSResult returned
fn runtimeEvaluate(
alloc: std.mem.Allocator,
id: u16,
env: jsruntime.Env,
script: []const u8,
comptime name: []const u8,
) !jsruntime.JSValue {
// try catch
var try_catch: jsruntime.TryCatch = undefined;
try_catch.init(env);
defer try_catch.deinit();
// script exec
const res = env.execWait(script, name) catch {
if (try try_catch.err(alloc, env)) |err_msg| {
defer alloc.free(err_msg);
std.log.err("'{s}' id {d}, result: {s}", .{ name, id, err_msg });
}
return error.CDPRuntimeEvaluate;
};
if (builtin.mode == .Debug) {
const res_msg = try res.toString(alloc, env);
defer alloc.free(res_msg);
std.log.debug("'{s}' id {d}, result: {s}", .{ name, id, res_msg });
}
return res;
}

View File

@@ -86,7 +86,7 @@ fn setAutoAttach(
if (msg.sessionID == null) { if (msg.sessionID == null) {
const attached = AttachToTarget{ const attached = AttachToTarget{
.sessionId = cdp.SessionID, .sessionId = cdp.BrowserSessionID,
.targetInfo = .{ .targetInfo = .{
.targetId = PageTargetID, .targetId = PageTargetID,
.title = "New Incognito tab", .title = "New Incognito tab",
@@ -160,7 +160,6 @@ fn getBrowserContexts(
} }
const ContextID = "22648B09EDCCDD11109E2D4FEFBE4F89"; const ContextID = "22648B09EDCCDD11109E2D4FEFBE4F89";
const ContextSessionID = "4FDC2CB760A23A220497A05C95417CF4";
fn createBrowserContext( fn createBrowserContext(
alloc: std.mem.Allocator, alloc: std.mem.Allocator,
@@ -218,7 +217,7 @@ fn createTarget(
// send attachToTarget event // send attachToTarget event
const attached = AttachToTarget{ const attached = AttachToTarget{
.sessionId = ContextSessionID, .sessionId = cdp.ContextSessionID,
.targetInfo = .{ .targetInfo = .{
.targetId = ctx.state.frameID, .targetId = ctx.state.frameID,
.title = "", .title = "",

View File

@@ -161,7 +161,7 @@ pub fn main() !void {
defer srv.close(); defer srv.close();
std.debug.print("Listening on: {s}...\n", .{socket_path}); std.debug.print("Listening on: {s}...\n", .{socket_path});
var browser = try Browser.init(arena.allocator(), vm); var browser = try Browser.init(arena.allocator());
defer browser.deinit(); defer browser.deinit();
try server.listen(&browser, srv.sockfd.?); try server.listen(&browser, srv.sockfd.?);

View File

@@ -80,13 +80,13 @@ pub fn main() !void {
const vm = jsruntime.VM.init(); const vm = jsruntime.VM.init();
defer vm.deinit(); defer vm.deinit();
var browser = try Browser.init(allocator, vm); var browser = try Browser.init(allocator);
defer browser.deinit(); defer browser.deinit();
var page = try browser.currentSession().createPage(); var page = try browser.currentSession().createPage();
defer page.deinit(); defer page.deinit();
try page.navigate(url); try page.navigate(url, null);
defer page.end(); defer page.end();
try page.wait(); try page.wait();

View File

@@ -70,16 +70,20 @@ pub const Cmd = struct {
} }
// shortcuts // shortcuts
fn alloc(self: *Cmd) std.mem.Allocator { inline fn alloc(self: *Cmd) std.mem.Allocator {
// TODO: should we return the allocator from the page instead? // TODO: should we return the allocator from the page instead?
return self.browser.currentSession().alloc; return self.browser.currentSession().alloc;
} }
fn loop(self: *Cmd) public.Loop { inline fn loop(self: *Cmd) public.Loop {
// TODO: pointer instead? // TODO: pointer instead?
return self.browser.currentSession().loop; return self.browser.currentSession().loop;
} }
inline fn env(self: Cmd) public.Env {
return self.browser.currentSession().env;
}
fn do(self: *Cmd, cmd: []const u8) anyerror!void { fn do(self: *Cmd, cmd: []const u8) anyerror!void {
const res = try cdp.do(self.alloc(), cmd, self); const res = try cdp.do(self.alloc(), cmd, self);
@@ -89,6 +93,49 @@ pub const Cmd = struct {
return sendAsync(self, res); return sendAsync(self, res);
} }
} }
// Inspector
pub fn sendInspector(self: *Cmd, msg: []const u8) void {
if (self.env().getInspector()) |inspector| {
inspector.send(self.env(), msg);
}
}
pub fn onInspectorResp(cmd_opaque: *anyopaque, _: u32, msg: []const u8) void {
std.log.debug("onResp biz fn called: {s}", .{msg});
const aligned = @as(*align(@alignOf(Cmd)) anyopaque, @alignCast(cmd_opaque));
const self = @as(*Cmd, @ptrCast(aligned));
const tpl = "{s},\"sessionId\":\"{s}\"}}";
const msg_open = msg[0 .. msg.len - 1]; // remove closing bracket
const s = std.fmt.allocPrint(
self.alloc(),
tpl,
.{ msg_open, cdp.ContextSessionID },
) catch unreachable;
defer self.alloc().free(s);
sendSync(self, s) catch unreachable;
}
pub fn onInspectorNotif(cmd_opaque: *anyopaque, msg: []const u8) void {
std.log.debug("onNotif biz fn called: {s}", .{msg});
const aligned = @as(*align(@alignOf(Cmd)) anyopaque, @alignCast(cmd_opaque));
const self = @as(*Cmd, @ptrCast(aligned));
const tpl = "{s},\"sessionId\":\"{s}\"}}";
const msg_open = msg[0 .. msg.len - 1]; // remove closing bracket
const s = std.fmt.allocPrint(
self.alloc(),
tpl,
.{ msg_open, cdp.ContextSessionID },
) catch unreachable;
defer self.alloc().free(s);
std.log.debug("event: {s}", .{s});
sendSync(self, s) catch unreachable;
}
}; };
// I/O Send // I/O Send
@@ -195,6 +242,9 @@ pub fn listen(browser: *Browser, socket: std.posix.socket_t) anyerror!void {
.buf = &ctxInput, .buf = &ctxInput,
.msg_buf = &msg_buf, .msg_buf = &msg_buf,
}; };
const cmd_opaque = @as(*anyopaque, @ptrCast(&cmd));
try browser.currentSession().setInspector(cmd_opaque, Cmd.onInspectorResp, Cmd.onInspectorNotif);
var accept = Accept{ var accept = Accept{
.cmd = &cmd, .cmd = &cmd,
.socket = socket, .socket = socket,