From 0d8dd84df5bb1083fb007d38403107625832ac13 Mon Sep 17 00:00:00 2001 From: Pierre Tachoire Date: Tue, 9 Dec 2025 10:07:32 +0100 Subject: [PATCH] support url on createTarget and send lifecycle events Support url parameter on createTarget. we now navigate on createTarget to dispatch events correctly, even in case of about:blank --- src/Notification.zig | 4 ++-- src/browser/Page.zig | 4 ++-- src/cdp/domains/page.zig | 34 ++++++++++++++++++++++++++++++---- src/cdp/domains/target.zig | 12 ++++++++---- 4 files changed, 42 insertions(+), 12 deletions(-) diff --git a/src/Notification.zig b/src/Notification.zig index f535abd9..dea1d549 100644 --- a/src/Notification.zig +++ b/src/Notification.zig @@ -109,13 +109,13 @@ pub const PageRemove = struct {}; pub const PageNavigate = struct { timestamp: u64, - url: []const u8, + url: [:0]const u8, opts: Page.NavigateOpts, }; pub const PageNavigated = struct { timestamp: u64, - url: []const u8, + url: [:0]const u8, }; pub const PageNetworkIdle = struct { diff --git a/src/browser/Page.zig b/src/browser/Page.zig index af235a5b..96b9f406 100644 --- a/src/browser/Page.zig +++ b/src/browser/Page.zig @@ -63,7 +63,7 @@ const timestamp = @import("../datetime.zig").timestamp; const milliTimestamp = @import("../datetime.zig").milliTimestamp; pub threadlocal var current: *Page = undefined; -var default_url = URL{ ._raw = "about/blank" }; +var default_url = URL{ ._raw = "about:blank" }; pub var default_location: Location = Location{ ._url = &default_url }; pub const BUF_SIZE = 1024; @@ -201,7 +201,7 @@ fn reset(self: *Page, comptime initializing: bool) !void { self._factory = Factory.init(self); self.version = 0; - self.url = "about/blank"; + self.url = "about:blank"; self.document = (try self._factory.document(Node.Document.HTMLDocument{ ._proto = undefined })).asDocument(); diff --git a/src/cdp/domains/page.zig b/src/cdp/domains/page.zig index 06b9a861..1be6f3c9 100644 --- a/src/cdp/domains/page.zig +++ b/src/cdp/domains/page.zig @@ -19,6 +19,7 @@ const std = @import("std"); const Page = @import("../../browser/Page.zig"); const Notification = @import("../../Notification.zig"); +const timestampF = @import("../../datetime.zig").timestamp; const Allocator = std.mem.Allocator; @@ -47,7 +48,7 @@ pub fn processMessage(cmd: anytype) !void { const Frame = struct { id: []const u8, loaderId: []const u8, - url: []const u8, + url: [:0]const u8, domainAndRegistry: []const u8 = "", securityOrigin: []const u8, mimeType: []const u8 = "text/html", @@ -82,11 +83,36 @@ 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) { + const now = timestampF(.monotonic); + const http_client = page._session.browser.http_client; + + try sendPageLifecycle(bc, "DOMContentLoaded", now); + try sendPageLifecycle(bc, "load", now); + + const http_active = http_client.active; + const total_network_activity = http_active + http_client.intercepted; + if (page._notified_network_almost_idle.check(total_network_activity <= 2)) { + try sendPageLifecycle(bc, "networkAlmostIdle", now); + } + if (page._notified_network_idle.check(total_network_activity == 0)) { + try sendPageLifecycle(bc, "networkIdle", now); + } + } + return cmd.sendResult(null, .{}); } diff --git a/src/cdp/domains/target.zig b/src/cdp/domains/target.zig index 3ea78b71..b59285af 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: [:0]const u8 = "about:blank", // width: ?u64 = null, // height: ?u64 = null, browserContextId: ?[]const u8 = null, @@ -168,7 +168,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", }, @@ -179,6 +179,10 @@ fn createTarget(cmd: anytype) !void { try doAttachtoTarget(cmd, target_id); } + try page.navigate(params.url, .{ + .reason = .address_bar, + }); + try cmd.sendResult(.{ .targetId = target_id, }, .{}); @@ -518,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.?; @@ -530,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.?;