diff --git a/src/browser/page.zig b/src/browser/page.zig index e33e1c7a..2175f884 100644 --- a/src/browser/page.zig +++ b/src/browser/page.zig @@ -83,6 +83,7 @@ pub const Page = struct { // indicates intention to navigate to another page on the next loop execution. delayed_navigation: bool = false, + navigated_options: ?NavigatedOpts = null, state_pool: *std.heap.MemoryPool(State), @@ -574,6 +575,11 @@ pub const Page = struct { }); self.session.browser.notification.dispatch(.page_navigated, &.{ + .opts = .{ + .cdp_id = opts.cdp_id, + .reason = opts.reason, + .method = opts.method, + }, .url = request_url, .timestamp = timestamp(), }); @@ -584,6 +590,12 @@ pub const Page = struct { const owned_url = try self.arena.dupeZ(u8, request_url); self.url = try URL.parse(owned_url, null); + self.navigated_options = .{ + .cdp_id = opts.cdp_id, + .reason = opts.reason, + .method = opts.method, + }; + var headers = try self.http_client.newHeaders(); if (opts.header) |hdr| try headers.add(hdr); try self.requestCookie(.{ .is_navigation = true }).headersForRequest(self.arena, owned_url, &headers); @@ -656,7 +668,9 @@ pub const Page = struct { log.err(.browser, "document is complete", .{ .err = err }); }; + std.debug.assert(self.navigated_options != null); self.session.browser.notification.dispatch(.page_navigated, &.{ + .opts = self.navigated_options.?, .url = self.url.raw, .timestamp = timestamp(), }); @@ -1264,6 +1278,12 @@ pub const NavigateOpts = struct { force: bool = false, }; +pub const NavigatedOpts = struct { + cdp_id: ?i64 = null, + reason: NavigateReason = .address_bar, + method: Http.Method = .GET, +}; + const IdleNotification = union(enum) { // hasn't started yet. init, diff --git a/src/cdp/cdp.zig b/src/cdp/cdp.zig index def56773..635f0a3a 100644 --- a/src/cdp/cdp.zig +++ b/src/cdp/cdp.zig @@ -545,13 +545,13 @@ pub fn BrowserContext(comptime CDP_T: type) type { pub fn onPageNavigate(ctx: *anyopaque, msg: *const Notification.PageNavigate) !void { const self: *Self = @ptrCast(@alignCast(ctx)); - defer self.resetNotificationArena(); - return @import("domains/page.zig").pageNavigate(self.notification_arena, self, msg); + return @import("domains/page.zig").pageNavigate(self, msg); } pub fn onPageNavigated(ctx: *anyopaque, msg: *const Notification.PageNavigated) !void { const self: *Self = @ptrCast(@alignCast(ctx)); - return @import("domains/page.zig").pageNavigated(self, msg); + defer self.resetNotificationArena(); + return @import("domains/page.zig").pageNavigated(self.notification_arena, self, msg); } pub fn onPageNetworkIdle(ctx: *anyopaque, msg: *const Notification.PageNetworkIdle) !void { diff --git a/src/cdp/domains/page.zig b/src/cdp/domains/page.zig index 93cf5a26..91e37fb7 100644 --- a/src/cdp/domains/page.zig +++ b/src/cdp/domains/page.zig @@ -184,7 +184,7 @@ fn navigate(cmd: anytype) !void { }); } -pub fn pageNavigate(arena: Allocator, bc: anytype, event: *const Notification.PageNavigate) !void { +pub fn pageNavigate(bc: anytype, event: *const Notification.PageNavigate) !void { // detachTarget could be called, in which case, we still have a page doing // things, but no session. const session_id = bc.session_id orelse return; @@ -234,6 +234,30 @@ pub fn pageNavigate(arena: Allocator, bc: anytype, event: *const Notification.Pa try cdp.sendEvent("Page.frameStartedLoading", .{ .frameId = target_id, }, .{ .session_id = session_id }); +} + +pub fn pageRemove(bc: anytype) !void { + // The main page is going to be removed, we need to remove contexts from other worlds first. + for (bc.isolated_worlds.items) |*isolated_world| { + try isolated_world.removeContext(); + } +} + +pub fn pageCreated(bc: anytype, page: *Page) !void { + for (bc.isolated_worlds.items) |*isolated_world| { + try isolated_world.createContextAndLoadPolyfills(bc.arena, page); + } +} + +pub fn pageNavigated(arena: Allocator, bc: anytype, event: *const Notification.PageNavigated) !void { + // detachTarget could be called, in which case, we still have a page doing + // things, but no session. + const session_id = bc.session_id orelse return; + const loader_id = bc.loader_id; + const target_id = bc.target_id orelse unreachable; + const timestamp = event.timestamp; + + var cdp = bc.cdp; // Drivers are sensitive to the order of events. Some more than others. // The result for the Page.navigate seems like it _must_ come after @@ -260,6 +284,17 @@ pub fn pageNavigate(arena: Allocator, bc: anytype, event: *const Notification.Pa }, .{ .session_id = session_id }); } + const reason_: ?[]const u8 = switch (event.opts.reason) { + .anchor => "anchorClick", + .script, .history, .navigation => "scriptInitiated", + .form => switch (event.opts.method) { + .GET => "formSubmissionGet", + .POST => "formSubmissionPost", + else => unreachable, + }, + .address_bar => null, + }; + if (reason_ != null) { try cdp.sendEvent("Page.frameClearedScheduledNavigation", .{ .frameId = target_id, @@ -293,30 +328,7 @@ pub fn pageNavigate(arena: Allocator, bc: anytype, event: *const Notification.Pa false, ); } -} -pub fn pageRemove(bc: anytype) !void { - // The main page is going to be removed, we need to remove contexts from other worlds first. - for (bc.isolated_worlds.items) |*isolated_world| { - try isolated_world.removeContext(); - } -} - -pub fn pageCreated(bc: anytype, page: *Page) !void { - for (bc.isolated_worlds.items) |*isolated_world| { - try isolated_world.createContextAndLoadPolyfills(bc.arena, page); - } -} - -pub fn pageNavigated(bc: anytype, event: *const Notification.PageNavigated) !void { - // detachTarget could be called, in which case, we still have a page doing - // things, but no session. - const session_id = bc.session_id orelse return; - const loader_id = bc.loader_id; - const target_id = bc.target_id orelse unreachable; - const timestamp = event.timestamp; - - var cdp = bc.cdp; // frameNavigated event try cdp.sendEvent("Page.frameNavigated", .{ .type = "Navigation", diff --git a/src/notification.zig b/src/notification.zig index 05024166..70d48bbe 100644 --- a/src/notification.zig +++ b/src/notification.zig @@ -98,6 +98,7 @@ pub const Notification = struct { pub const PageNavigated = struct { timestamp: u32, url: []const u8, + opts: page.NavigatedOpts, }; pub const PageNetworkIdle = struct { @@ -326,7 +327,7 @@ test "Notification" { .url = undefined, .opts = .{}, }); - notifier.dispatch(.page_navigated, &.{ .timestamp = 6, .url = undefined }); + notifier.dispatch(.page_navigated, &.{ .timestamp = 6, .url = undefined, .opts = .{} }); try testing.expectEqual(14, tc.page_navigate); try testing.expectEqual(6, tc.page_navigated); @@ -336,7 +337,7 @@ test "Notification" { .url = undefined, .opts = .{}, }); - notifier.dispatch(.page_navigated, &.{ .timestamp = 100, .url = undefined }); + notifier.dispatch(.page_navigated, &.{ .timestamp = 100, .url = undefined, .opts = .{} }); try testing.expectEqual(14, tc.page_navigate); try testing.expectEqual(6, tc.page_navigated); @@ -345,26 +346,26 @@ test "Notification" { try notifier.register(.page_navigate, &tc, TestClient.pageNavigate); try notifier.register(.page_navigated, &tc, TestClient.pageNavigated); notifier.dispatch(.page_navigate, &.{ .timestamp = 100, .url = undefined, .opts = .{} }); - notifier.dispatch(.page_navigated, &.{ .timestamp = 1000, .url = undefined }); + notifier.dispatch(.page_navigated, &.{ .timestamp = 1000, .url = undefined, .opts = .{} }); try testing.expectEqual(114, tc.page_navigate); try testing.expectEqual(1006, tc.page_navigated); notifier.unregister(.page_navigate, &tc); notifier.dispatch(.page_navigate, &.{ .timestamp = 100, .url = undefined, .opts = .{} }); - notifier.dispatch(.page_navigated, &.{ .timestamp = 1000, .url = undefined }); + notifier.dispatch(.page_navigated, &.{ .timestamp = 1000, .url = undefined, .opts = .{} }); try testing.expectEqual(114, tc.page_navigate); try testing.expectEqual(2006, tc.page_navigated); notifier.unregister(.page_navigated, &tc); notifier.dispatch(.page_navigate, &.{ .timestamp = 100, .url = undefined, .opts = .{} }); - notifier.dispatch(.page_navigated, &.{ .timestamp = 1000, .url = undefined }); + notifier.dispatch(.page_navigated, &.{ .timestamp = 1000, .url = undefined, .opts = .{} }); try testing.expectEqual(114, tc.page_navigate); try testing.expectEqual(2006, tc.page_navigated); // already unregistered, try anyways notifier.unregister(.page_navigated, &tc); notifier.dispatch(.page_navigate, &.{ .timestamp = 100, .url = undefined, .opts = .{} }); - notifier.dispatch(.page_navigated, &.{ .timestamp = 1000, .url = undefined }); + notifier.dispatch(.page_navigated, &.{ .timestamp = 1000, .url = undefined, .opts = .{} }); try testing.expectEqual(114, tc.page_navigate); try testing.expectEqual(2006, tc.page_navigated); }