From a7c3bad9ad385bd913bb88850b41b2b0ba56c8b6 Mon Sep 17 00:00:00 2001 From: Pierre Tachoire Date: Tue, 21 Oct 2025 17:45:19 +0200 Subject: [PATCH 1/5] cdp: implement url parameter on createTarget --- src/browser/page.zig | 14 +++++++++++++- src/cdp/domains/target.zig | 13 +++++++++---- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/src/browser/page.zig b/src/browser/page.zig index 833c683f..5bfd6374 100644 --- a/src/browser/page.zig +++ b/src/browser/page.zig @@ -556,7 +556,19 @@ pub const Page = struct { // We do not processHTMLDoc here as we know we don't have any scripts // This assumption may be false when CDP Page.addScriptToEvaluateOnNewDocument is implemented - try HTMLDocument.documentIsComplete(self.window.document, self); + self.documentIsComplete(); + + self.session.browser.notification.dispatch(.page_navigate, &.{ + .opts = opts, + .url = request_url, + .timestamp = timestamp(), + }); + + self.session.browser.notification.dispatch(.page_navigated, &.{ + .url = request_url, + .timestamp = timestamp(), + }); + return; } diff --git a/src/cdp/domains/target.zig b/src/cdp/domains/target.zig index 26f4cfbe..e58f278d 100644 --- a/src/cdp/domains/target.zig +++ b/src/cdp/domains/target.zig @@ -109,7 +109,7 @@ fn disposeBrowserContext(cmd: anytype) !void { fn createTarget(cmd: anytype) !void { const params = (try cmd.params(struct { - // url: []const u8, + url: []const u8 = "about:blank", // width: ?u64 = null, // height: ?u64 = null, browserContextId: ?[]const u8 = null, @@ -167,7 +167,7 @@ fn createTarget(cmd: anytype) !void { .targetInfo = TargetInfo{ .attached = false, .targetId = target_id, - .title = "about:blank", + .title = params.url, .browserContextId = bc.id, .url = "about:blank", }, @@ -178,6 +178,11 @@ fn createTarget(cmd: anytype) !void { try doAttachtoTarget(cmd, target_id); } + try page.navigate(params.url, .{ + .reason = .address_bar, + .cdp_id = cmd.input.id, + }); + try cmd.sendResult(.{ .targetId = target_id, }, .{}); @@ -517,7 +522,7 @@ test "cdp.target: createTarget" { { var ctx = testing.context(); defer ctx.deinit(); - try ctx.processMessage(.{ .id = 10, .method = "Target.createTarget", .params = .{ .url = "about/blank" } }); + try ctx.processMessage(.{ .id = 10, .method = "Target.createTarget", .params = .{ .url = "about:blank" } }); // should create a browser context const bc = ctx.cdp().browser_context.?; @@ -529,7 +534,7 @@ test "cdp.target: createTarget" { defer ctx.deinit(); // active auto attach to get the Target.attachedToTarget event. try ctx.processMessage(.{ .id = 9, .method = "Target.setAutoAttach", .params = .{ .autoAttach = true, .waitForDebuggerOnStart = false } }); - try ctx.processMessage(.{ .id = 10, .method = "Target.createTarget", .params = .{ .url = "about/blank" } }); + try ctx.processMessage(.{ .id = 10, .method = "Target.createTarget", .params = .{ .url = "about:blank" } }); // should create a browser context const bc = ctx.cdp().browser_context.?; From 2d8a95946ac21b9603058826348dccbe170e8801 Mon Sep 17 00:00:00 2001 From: Pierre Tachoire Date: Tue, 21 Oct 2025 17:48:51 +0200 Subject: [PATCH 2/5] cdp: dispatch lifecycle events when enable --- src/cdp/domains/page.zig | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/src/cdp/domains/page.zig b/src/cdp/domains/page.zig index 1f6b720a..91656103 100644 --- a/src/cdp/domains/page.zig +++ b/src/cdp/domains/page.zig @@ -18,6 +18,7 @@ const std = @import("std"); const Page = @import("../../browser/page.zig").Page; +const timestampF = @import("../../datetime.zig").timestamp; const Notification = @import("../../notification.zig").Notification; const Allocator = std.mem.Allocator; @@ -82,11 +83,33 @@ fn setLifecycleEventsEnabled(cmd: anytype) !void { })) orelse return error.InvalidParams; const bc = cmd.browser_context orelse return error.BrowserContextNotLoaded; - if (params.enabled) { - try bc.lifecycleEventsEnable(); - } else { + + if (params.enabled == false) { bc.lifecycleEventsDisable(); + return cmd.sendResult(null, .{}); } + + // Enable lifecycle events. + try bc.lifecycleEventsEnable(); + + // When we enable lifecycle events, we must dispatch events for all + // attached targets. + const page = bc.session.currentPage() orelse return error.PageNotLoaded; + + if (page.load_state == .complete) { + try sendPageLifecycle(bc, "DOMContentLoaded", timestampF()); + try sendPageLifecycle(bc, "load", timestampF()); + + const http_active = page.http_client.active; + const total_network_activity = http_active + page.http_client.intercepted; + if (page.notified_network_almost_idle.check(total_network_activity <= 2)) { + try sendPageLifecycle(bc, "networkAlmostIdle", timestampF()); + } + if (page.notified_network_idle.check(total_network_activity == 0)) { + try sendPageLifecycle(bc, "networkIdle", timestampF()); + } + } + return cmd.sendResult(null, .{}); } From 43958b81f8ab2b1adbfb63ea65f54b6365e0c841 Mon Sep 17 00:00:00 2001 From: Pierre Tachoire Date: Tue, 21 Oct 2025 17:50:11 +0200 Subject: [PATCH 3/5] http: remove inflight conn check chromiumoxide sends the command while connections are in progress and it doesn't cause issue w/ curl. --- src/http/Client.zig | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/http/Client.zig b/src/http/Client.zig index 40647336..588e0c4f 100644 --- a/src/http/Client.zig +++ b/src/http/Client.zig @@ -338,8 +338,6 @@ pub fn restoreOriginalProxy(self: *Client) !void { // Enable TLS verification on all connections. pub fn enableTlsVerify(self: *const Client) !void { - try self.ensureNoActiveConnection(); - for (self.handles.handles) |*h| { const easy = h.conn.easy; @@ -355,8 +353,6 @@ pub fn enableTlsVerify(self: *const Client) !void { // Disable TLS verification on all connections. pub fn disableTlsVerify(self: *const Client) !void { - try self.ensureNoActiveConnection(); - for (self.handles.handles) |*h| { const easy = h.conn.easy; From a69164b48294df6843f311a5725f0dcc86304cb8 Mon Sep 17 00:00:00 2001 From: Pierre Tachoire Date: Wed, 22 Oct 2025 12:08:27 +0200 Subject: [PATCH 4/5] page: fix page mode when loading about:blank --- src/browser/page.zig | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/browser/page.zig b/src/browser/page.zig index 5bfd6374..93361a33 100644 --- a/src/browser/page.zig +++ b/src/browser/page.zig @@ -549,11 +549,16 @@ pub const Page = struct { .body = opts.body != null, }); - // if the url is about:blank, nothing to do. + // if the url is about:blank, we load an empty HTML document in the + // page and dispatch the events. if (std.mem.eql(u8, "about:blank", request_url)) { const html_doc = try parser.documentHTMLParseFromStr(""); try self.setDocument(html_doc); + // Assume we parsed the document. + // It's important to force a reset during the following navigation. + self.mode = .parsed; + // We do not processHTMLDoc here as we know we don't have any scripts // This assumption may be false when CDP Page.addScriptToEvaluateOnNewDocument is implemented self.documentIsComplete(); From b5ef8418a67b9522e0b8dd7288d64d0c25178b55 Mon Sep 17 00:00:00 2001 From: Pierre Tachoire Date: Wed, 22 Oct 2025 14:18:53 +0200 Subject: [PATCH 5/5] cdp: fix double createTarget response --- src/cdp/domains/target.zig | 1 - 1 file changed, 1 deletion(-) diff --git a/src/cdp/domains/target.zig b/src/cdp/domains/target.zig index e58f278d..2d7c3b02 100644 --- a/src/cdp/domains/target.zig +++ b/src/cdp/domains/target.zig @@ -180,7 +180,6 @@ fn createTarget(cmd: anytype) !void { try page.navigate(params.url, .{ .reason = .address_bar, - .cdp_id = cmd.input.id, }); try cmd.sendResult(.{