mirror of
https://github.com/lightpanda-io/browser.git
synced 2025-12-17 08:48:58 +00:00
Redirect Runtime domain to JS engine Inspector
Signed-off-by: Francis Bouvier <francis@lightpanda.io>
This commit is contained in:
@@ -16,6 +16,7 @@ const Methods = enum {
|
||||
evaluate,
|
||||
addBinding,
|
||||
callFunctionOn,
|
||||
releaseObject,
|
||||
};
|
||||
|
||||
pub fn runtime(
|
||||
@@ -23,44 +24,83 @@ pub fn runtime(
|
||||
id: ?u16,
|
||||
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) {
|
||||
.enable => enable(alloc, id, scanner, ctx),
|
||||
.runIfWaitingForDebugger => runIfWaitingForDebugger(alloc, id, scanner, ctx),
|
||||
.evaluate => evaluate(alloc, id, scanner, ctx),
|
||||
.addBinding => addBinding(alloc, id, scanner, ctx),
|
||||
.callFunctionOn => callFunctionOn(alloc, id, scanner, ctx),
|
||||
else => sendInspector(alloc, method, id, s, scanner, ctx),
|
||||
};
|
||||
}
|
||||
|
||||
fn enable(
|
||||
fn sendInspector(
|
||||
alloc: std.mem.Allocator,
|
||||
id: ?u16,
|
||||
method: Methods,
|
||||
_id: ?u16,
|
||||
s: []const u8,
|
||||
scanner: *std.json.Scanner,
|
||||
_: *Ctx,
|
||||
ctx: *Ctx,
|
||||
) ![]const u8 {
|
||||
|
||||
// input
|
||||
const msg = try getMsg(alloc, void, scanner);
|
||||
// save script in file at debug mode
|
||||
if (std.log.defaultLogEnabled(.debug)) {
|
||||
|
||||
// output
|
||||
// const uniqueID = "1367118932354479079.-1471398151593995849";
|
||||
// const mainCtx = try executionContextCreated(
|
||||
// alloc,
|
||||
// 1,
|
||||
// cdp.URLBase,
|
||||
// "",
|
||||
// uniqueID,
|
||||
// .{},
|
||||
// sessionID,
|
||||
// );
|
||||
// std.log.debug("res {s}", .{mainCtx});
|
||||
// try server.sendAsync(ctx, mainCtx);
|
||||
// input
|
||||
var script: ?[]const u8 = null;
|
||||
var id: u16 = undefined;
|
||||
|
||||
return result(alloc, id orelse msg.id.?, null, null, msg.sessionID);
|
||||
if (method == .evaluate) {
|
||||
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);
|
||||
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 {
|
||||
@@ -69,14 +109,6 @@ pub const AuxData = struct {
|
||||
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(
|
||||
alloc: std.mem.Allocator,
|
||||
ctx: *Ctx,
|
||||
@@ -88,7 +120,13 @@ pub fn executionContextCreated(
|
||||
sessionID: ?[]const u8,
|
||||
) !void {
|
||||
const Params = struct {
|
||||
context: ExecutionContextDescription,
|
||||
context: struct {
|
||||
id: u64,
|
||||
origin: []const u8,
|
||||
name: []const u8,
|
||||
uniqueId: []const u8,
|
||||
auxData: ?AuxData = null,
|
||||
},
|
||||
};
|
||||
const params = Params{
|
||||
.context = .{
|
||||
@@ -112,195 +150,3 @@ fn runIfWaitingForDebugger(
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user