Merge pull request #285 from lightpanda-io/cdp-cdpcli
Some checks failed
wpt / web platform tests (push) Has been cancelled
zig-test / zig build dev (push) Has been cancelled
zig-test / zig build release (push) Has been cancelled
zig-test / zig test (push) Has been cancelled
wpt / perf-fmt (push) Has been cancelled
zig-test / perf-fmt (push) Has been cancelled
zig-test / demo-puppeteer (push) Has been cancelled

cdp: cdpcli compatibility
This commit is contained in:
Pierre Tachoire
2025-01-13 18:16:40 +01:00
committed by GitHub
7 changed files with 88 additions and 33 deletions

View File

@@ -214,6 +214,31 @@ pub const Page = struct {
}; };
} }
// start js env.
// - auxData: extra data forwarded to the Inspector
// see Inspector.contextCreated
pub fn start(self: *Page, auxData: ?[]const u8) !void {
// start JS env
log.debug("start js env", .{});
try self.session.env.start();
// register the module loader
try self.session.env.setModuleLoadFn(self.session, Session.fetchModule);
// add global objects
log.debug("setup global env", .{});
try self.session.env.bindGlobal(&self.session.window);
// load polyfills
try polyfill.load(self.arena.allocator(), self.session.env);
// inspector
if (self.session.inspector) |inspector| {
log.debug("inspector context created", .{});
inspector.contextCreated(self.session.env, "", self.origin orelse "://", auxData);
}
}
// reset js env and mem arena. // reset js env and mem arena.
pub fn end(self: *Page) void { pub fn end(self: *Page) void {
self.session.env.stop(); self.session.env.stop();
@@ -373,17 +398,6 @@ pub const Page = struct {
// https://html.spec.whatwg.org/#read-html // https://html.spec.whatwg.org/#read-html
// start JS env
// TODO load the js env concurrently with the HTML parsing.
log.debug("start js env", .{});
try self.session.env.start();
// register the module loader
try self.session.env.setModuleLoadFn(self.session, Session.fetchModule);
// load polyfills
try polyfill.load(alloc, self.session.env);
// inspector // inspector
if (self.session.inspector) |inspector| { if (self.session.inspector) |inspector| {
inspector.contextCreated(self.session.env, "", self.origin.?, auxData); inspector.contextCreated(self.session.env, "", self.origin.?, auxData);
@@ -395,10 +409,6 @@ pub const Page = struct {
.httpClient = &self.session.httpClient, .httpClient = &self.session.httpClient,
}); });
// add global objects
log.debug("setup global env", .{});
try self.session.env.bindGlobal(&self.session.window);
// browse the DOM tree to retrieve scripts // browse the DOM tree to retrieve scripts
// TODO execute the synchronous scripts during the HTL parsing. // TODO execute the synchronous scripts during the HTL parsing.
// TODO fetch the script resources concurrently but execute them in the // TODO fetch the script resources concurrently but execute them in the

View File

@@ -121,6 +121,7 @@ pub fn dispatch(
pub const State = struct { pub const State = struct {
executionContextId: u32 = 0, executionContextId: u32 = 0,
contextID: ?[]const u8 = null, contextID: ?[]const u8 = null,
sessionID: ?[]const u8 = null,
frameID: []const u8 = FrameID, frameID: []const u8 = FrameID,
url: []const u8 = URLBase, url: []const u8 = URLBase,
securityOrigin: []const u8 = URLBase, securityOrigin: []const u8 = URLBase,
@@ -221,11 +222,11 @@ pub fn sendEvent(
// ------ // ------
// TODO: hard coded IDs // TODO: hard coded IDs
pub const BrowserSessionID = "9559320D92474062597D9875C664CAC0"; pub const BrowserSessionID = "BROWSERSESSIONID597D9875C664CAC0";
pub const ContextSessionID = "4FDC2CB760A23A220497A05C95417CF4"; pub const ContextSessionID = "CONTEXTSESSIONID0497A05C95417CF4";
pub const URLBase = "chrome://newtab/"; pub const URLBase = "chrome://newtab/";
pub const FrameID = "90D14BBD8AED408A0467AC93100BCDBE"; pub const LoaderID = "LOADERID24DD2FD56CF1EF33C965C79C";
pub const LoaderID = "CFC8BED824DD2FD56CF1EF33C965C79C"; pub const FrameID = "FRAMEIDD8AED408A0467AC93100BCDBE";
pub const TimestampEvent = struct { pub const TimestampEvent = struct {
timestamp: f64, timestamp: f64,

View File

@@ -331,8 +331,9 @@ fn navigate(
// 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, {}, input.sessionId); try sendEvent(alloc, ctx, "Runtime.executionContextsCleared", void, {}, input.sessionId);
// Launch navigate // Launch navigate, the page must have been created by a
const p = try ctx.browser.session.createPage(); // target.createTarget.
var p = ctx.browser.session.page orelse return error.NoPage;
ctx.state.executionContextId += 1; ctx.state.executionContextId += 1;
const auxData = try std.fmt.allocPrint( const auxData = try std.fmt.allocPrint(
alloc, alloc,

View File

@@ -28,6 +28,7 @@ const result = cdp.result;
const IncomingMessage = @import("msg.zig").IncomingMessage; const IncomingMessage = @import("msg.zig").IncomingMessage;
const Input = @import("msg.zig").Input; const Input = @import("msg.zig").Input;
const stringify = cdp.stringify; const stringify = cdp.stringify;
const target = @import("target.zig");
const log = std.log.scoped(.cdp); const log = std.log.scoped(.cdp);
@@ -116,6 +117,8 @@ fn sendInspector(
} }
} }
ctx.state.sessionID = msg.sessionId;
// 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) {

View File

@@ -39,6 +39,7 @@ const Methods = enum {
createTarget, createTarget,
closeTarget, closeTarget,
sendMessageToTarget, sendMessageToTarget,
detachFromTarget,
}; };
pub fn target( pub fn target(
@@ -60,13 +61,14 @@ pub fn target(
.createTarget => createTarget(alloc, msg, ctx), .createTarget => createTarget(alloc, msg, ctx),
.closeTarget => closeTarget(alloc, msg, ctx), .closeTarget => closeTarget(alloc, msg, ctx),
.sendMessageToTarget => sendMessageToTarget(alloc, msg, ctx), .sendMessageToTarget => sendMessageToTarget(alloc, msg, ctx),
.detachFromTarget => detachFromTarget(alloc, msg, ctx),
}; };
} }
// TODO: hard coded IDs // TODO: hard coded IDs
const PageTargetID = "CFCD6EC01573CF29BB638E9DC0F52DDC"; pub const PageTargetID = "PAGETARGETIDB638E9DC0F52DDC";
const BrowserTargetID = "2d2bdef9-1c95-416f-8c0e-83f3ab73a30c"; pub const BrowserTargetID = "browser9-targ-et6f-id0e-83f3ab73a30c";
const BrowserContextID = "65618675CB7D3585A95049E9DFE95EA9"; pub const BrowserContextID = "BROWSERCONTEXTIDA95049E9DFE95EA9";
// TODO: noop method // TODO: noop method
fn setDiscoverTargets( fn setDiscoverTargets(
@@ -138,7 +140,7 @@ fn setAutoAttach(
.sessionId = cdp.BrowserSessionID, .sessionId = cdp.BrowserSessionID,
.targetInfo = .{ .targetInfo = .{
.targetId = PageTargetID, .targetId = PageTargetID,
.title = "New Incognito tab", .title = "about:blank",
.url = cdp.URLBase, .url = cdp.URLBase,
.browserContextId = BrowserContextID, .browserContextId = BrowserContextID,
}, },
@@ -171,8 +173,8 @@ fn attachToTarget(
const attached = AttachToTarget{ const attached = AttachToTarget{
.sessionId = cdp.BrowserSessionID, .sessionId = cdp.BrowserSessionID,
.targetInfo = .{ .targetInfo = .{
.targetId = PageTargetID, .targetId = input.params.targetId,
.title = "New Incognito tab", .title = "about:blank",
.url = cdp.URLBase, .url = cdp.URLBase,
.browserContextId = BrowserContextID, .browserContextId = BrowserContextID,
}, },
@@ -185,7 +187,7 @@ fn attachToTarget(
sessionId: []const u8, sessionId: []const u8,
}; };
const output = SessionId{ const output = SessionId{
.sessionId = input.sessionId orelse BrowserContextID, .sessionId = input.sessionId orelse cdp.BrowserSessionID,
}; };
return result(alloc, input.id, SessionId, output, null); return result(alloc, input.id, SessionId, output, null);
} }
@@ -252,7 +254,7 @@ fn getBrowserContexts(
return result(alloc, input.id, Resp, resp, null); return result(alloc, input.id, Resp, resp, null);
} }
const ContextID = "22648B09EDCCDD11109E2D4FEFBE4F89"; const ContextID = "CONTEXTIDDCCDD11109E2D4FEFBE4F89";
// TODO: noop method // TODO: noop method
fn createBrowserContext( fn createBrowserContext(
@@ -313,8 +315,8 @@ fn disposeBrowserContext(
} }
// TODO: hard coded IDs // TODO: hard coded IDs
const TargetID = "57356548460A8F29706A2ADF14316298"; const TargetID = "TARGETID460A8F29706A2ADF14316298";
const LoaderID = "DD4A76F842AA389647D702B4D805F49A"; const LoaderID = "LOADERID42AA389647D702B4D805F49A";
fn createTarget( fn createTarget(
alloc: std.mem.Allocator, alloc: std.mem.Allocator,
@@ -342,6 +344,23 @@ fn createTarget(
ctx.state.securityOrigin = "://"; ctx.state.securityOrigin = "://";
ctx.state.secureContextType = "InsecureScheme"; ctx.state.secureContextType = "InsecureScheme";
ctx.state.loaderID = LoaderID; ctx.state.loaderID = LoaderID;
ctx.state.sessionID = msg.sessionId;
// TODO stop the previous page instead?
if (ctx.browser.session.page != null) return error.pageAlreadyExists;
// create the page
const p = try ctx.browser.session.createPage();
ctx.state.executionContextId += 1;
// start the js env
const auxData = try std.fmt.allocPrint(
alloc,
// NOTE: we assume this is the default web page
"{{\"isDefault\":true,\"type\":\"default\",\"frameId\":\"{s}\"}}",
.{ctx.state.frameID},
);
defer alloc.free(auxData);
try p.start(auxData);
// send targetCreated event // send targetCreated event
const created = TargetCreated{ const created = TargetCreated{
@@ -361,9 +380,10 @@ fn createTarget(
.sessionId = cdp.ContextSessionID, .sessionId = cdp.ContextSessionID,
.targetInfo = .{ .targetInfo = .{
.targetId = ctx.state.frameID, .targetId = ctx.state.frameID,
.title = "", .title = "about:blank",
.url = ctx.state.url, .url = ctx.state.url,
.browserContextId = input.params.browserContextId orelse ContextID, .browserContextId = input.params.browserContextId orelse ContextID,
.attached = true,
}, },
.waitingForDebugger = true, .waitingForDebugger = true,
}; };
@@ -438,6 +458,8 @@ fn closeTarget(
null, null,
); );
if (ctx.browser.session.page != null) ctx.browser.session.page.?.end();
return ""; return "";
} }
@@ -484,3 +506,18 @@ fn sendMessageToTarget(
return ""; return "";
} }
// noop
fn detachFromTarget(
alloc: std.mem.Allocator,
msg: *IncomingMessage,
_: *Ctx,
) ![]const u8 {
// input
const input = try Input(void).get(alloc, msg);
defer input.deinit();
log.debug("Req > id {d}, method {s}", .{ input.id, "target.detachFromTarget" });
// output
return result(alloc, input.id, bool, true, input.sessionId);
}

View File

@@ -326,6 +326,8 @@ pub fn main() !void {
// page // page
const page = try browser.session.createPage(); const page = try browser.session.createPage();
try page.start(null);
defer page.end();
_ = page.navigate(opts.url, null) catch |err| switch (err) { _ = page.navigate(opts.url, null) catch |err| switch (err) {
error.UnsupportedUriScheme, error.UriMissingHost => { error.UnsupportedUriScheme, error.UriMissingHost => {

View File

@@ -175,6 +175,7 @@ pub const Ctx = struct {
self.do(parts.msg) catch |err| { self.do(parts.msg) catch |err| {
if (err != error.Closed) { if (err != error.Closed) {
log.err("do error: {any}", .{err}); log.err("do error: {any}", .{err});
log.debug("last msg: {s}", .{parts.msg});
} }
}; };
} }
@@ -347,7 +348,7 @@ pub const Ctx = struct {
const s = try std.fmt.allocPrint( const s = try std.fmt.allocPrint(
allocator, allocator,
tpl, tpl,
.{ msg_open, cdp.ContextSessionID }, .{ msg_open, ctx.state.sessionID orelse cdp.ContextSessionID },
); );
try ctx.send(s); try ctx.send(s);