diff --git a/src/browser/page.zig b/src/browser/page.zig index 2175f884..05bd528f 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, + req_id: ?usize = null, navigated_options: ?NavigatedOpts = null, state_pool: *std.heap.MemoryPool(State), @@ -547,11 +548,14 @@ pub const Page = struct { try self.reset(); } + const req_id = self.http_client.nextReqId(); + log.info(.http, "navigate", .{ .url = request_url, .method = opts.method, .reason = opts.reason, .body = opts.body != null, + .req_id = req_id, }); // if the url is about:blank, we load an empty HTML document in the @@ -569,12 +573,14 @@ pub const Page = struct { self.documentIsComplete(); self.session.browser.notification.dispatch(.page_navigate, &.{ + .req_id = req_id, .opts = opts, .url = request_url, .timestamp = timestamp(), }); self.session.browser.notification.dispatch(.page_navigated, &.{ + .req_id = req_id, .opts = .{ .cdp_id = opts.cdp_id, .reason = opts.reason, @@ -584,12 +590,16 @@ pub const Page = struct { .timestamp = timestamp(), }); + // force next request id manually b/c we won't create a real req. + _ = self.http_client.incrReqId(); + return; } const owned_url = try self.arena.dupeZ(u8, request_url); self.url = try URL.parse(owned_url, null); + self.req_id = req_id; self.navigated_options = .{ .cdp_id = opts.cdp_id, .reason = opts.reason, @@ -603,6 +613,7 @@ pub const Page = struct { // We dispatch page_navigate event before sending the request. // It ensures the event page_navigated is not dispatched before this one. self.session.browser.notification.dispatch(.page_navigate, &.{ + .req_id = req_id, .opts = opts, .url = owned_url, .timestamp = timestamp(), @@ -668,8 +679,10 @@ pub const Page = struct { log.err(.browser, "document is complete", .{ .err = err }); }; + std.debug.assert(self.req_id != null); std.debug.assert(self.navigated_options != null); self.session.browser.notification.dispatch(.page_navigated, &.{ + .req_id = self.req_id.?, .opts = self.navigated_options.?, .url = self.url.raw, .timestamp = timestamp(), diff --git a/src/cdp/cdp.zig b/src/cdp/cdp.zig index 635f0a3a..a7f46fcb 100644 --- a/src/cdp/cdp.zig +++ b/src/cdp/cdp.zig @@ -545,7 +545,8 @@ pub fn BrowserContext(comptime CDP_T: type) type { pub fn onPageNavigate(ctx: *anyopaque, msg: *const Notification.PageNavigate) !void { const self: *Self = @ptrCast(@alignCast(ctx)); - return @import("domains/page.zig").pageNavigate(self, msg); + defer self.resetNotificationArena(); + return @import("domains/page.zig").pageNavigate(self.notification_arena, self, msg); } pub fn onPageNavigated(ctx: *anyopaque, msg: *const Notification.PageNavigated) !void { diff --git a/src/cdp/domains/network.zig b/src/cdp/domains/network.zig index 9ad62809..3d32f191 100644 --- a/src/cdp/domains/network.zig +++ b/src/cdp/domains/network.zig @@ -243,11 +243,12 @@ pub fn httpRequestStart(arena: Allocator, bc: anytype, msg: *const Notification. } const transfer = msg.transfer; + const loader_id = try std.fmt.allocPrint(arena, "REQ-{d}", .{transfer.id}); // We're missing a bunch of fields, but, for now, this seems like enough try bc.cdp.sendEvent("Network.requestWillBeSent", .{ - .requestId = try std.fmt.allocPrint(arena, "REQ-{d}", .{transfer.id}), + .requestId = loader_id, .frameId = target_id, - .loaderId = bc.loader_id, + .loaderId = loader_id, .type = msg.transfer.req.resource_type.string(), .documentURL = DocumentUrlWriter.init(&page.url.uri), .request = TransferAsRequestWriter.init(transfer), @@ -263,11 +264,14 @@ pub fn httpResponseHeaderDone(arena: Allocator, bc: anytype, msg: *const Notific const session_id = bc.session_id orelse return; const target_id = bc.target_id orelse unreachable; + const transfer = msg.transfer; + const loader_id = try std.fmt.allocPrint(arena, "REQ-{d}", .{transfer.id}); + // We're missing a bunch of fields, but, for now, this seems like enough try bc.cdp.sendEvent("Network.responseReceived", .{ - .requestId = try std.fmt.allocPrint(arena, "REQ-{d}", .{msg.transfer.id}), - .loaderId = bc.loader_id, + .requestId = loader_id, .frameId = target_id, + .loaderId = loader_id, .response = TransferAsResponseWriter.init(arena, msg.transfer), .hasExtraInfo = false, // TODO change after adding Network.responseReceivedExtraInfo }, .{ .session_id = session_id }); diff --git a/src/cdp/domains/page.zig b/src/cdp/domains/page.zig index 91e37fb7..8a1e9702 100644 --- a/src/cdp/domains/page.zig +++ b/src/cdp/domains/page.zig @@ -176,7 +176,6 @@ fn navigate(cmd: anytype) !void { } var page = bc.session.currentPage() orelse return error.PageNotLoaded; - bc.loader_id = bc.cdp.loader_id_gen.next(); try page.navigate(params.url, .{ .reason = .address_bar, @@ -184,13 +183,12 @@ fn navigate(cmd: anytype) !void { }); } -pub fn pageNavigate(bc: anytype, event: *const Notification.PageNavigate) !void { +pub fn pageNavigate(arena: Allocator, 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; - bc.loader_id = bc.cdp.loader_id_gen.next(); - const loader_id = bc.loader_id; + const loader_id = try std.fmt.allocPrint(arena, "REQ-{d}", .{event.req_id}); const target_id = bc.target_id orelse unreachable; bc.reset(); @@ -253,7 +251,7 @@ pub fn pageNavigated(arena: Allocator, bc: anytype, event: *const Notification.P // 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 loader_id = try std.fmt.allocPrint(arena, "REQ-{d}", .{event.req_id}); const target_id = bc.target_id orelse unreachable; const timestamp = event.timestamp; @@ -335,7 +333,7 @@ pub fn pageNavigated(arena: Allocator, bc: anytype, event: *const Notification.P .frame = Frame{ .id = target_id, .url = event.url, - .loaderId = bc.loader_id, + .loaderId = loader_id, .securityOrigin = bc.security_origin, .secureContextType = bc.secure_context_type, }, diff --git a/src/http/Client.zig b/src/http/Client.zig index 9c564181..953df53b 100644 --- a/src/http/Client.zig +++ b/src/http/Client.zig @@ -261,6 +261,16 @@ pub fn fulfillTransfer(self: *Client, transfer: *Transfer, status: u16, headers: return transfer.fulfill(status, headers, body); } +pub fn nextReqId(self: *Client) usize { + return self.next_request_id + 1; +} + +pub fn incrReqId(self: *Client) usize { + const id = self.next_request_id + 1; + self.next_request_id = id; + return id; +} + fn makeTransfer(self: *Client, req: Request) !*Transfer { errdefer req.headers.deinit(); @@ -273,8 +283,7 @@ fn makeTransfer(self: *Client, req: Request) !*Transfer { const transfer = try self.transfer_pool.create(); errdefer self.transfer_pool.destroy(transfer); - const id = self.next_request_id + 1; - self.next_request_id = id; + const id = self.incrReqId(); transfer.* = .{ .arena = ArenaAllocator.init(self.allocator), .id = id, diff --git a/src/notification.zig b/src/notification.zig index 70d48bbe..5da980cf 100644 --- a/src/notification.zig +++ b/src/notification.zig @@ -90,12 +90,14 @@ pub const Notification = struct { pub const PageRemove = struct {}; pub const PageNavigate = struct { + req_id: usize, timestamp: u32, url: []const u8, opts: page.NavigateOpts, }; pub const PageNavigated = struct { + req_id: usize, timestamp: u32, url: []const u8, opts: page.NavigatedOpts, @@ -297,6 +299,7 @@ test "Notification" { // noop notifier.dispatch(.page_navigate, &.{ + .req_id = 1, .timestamp = 4, .url = undefined, .opts = .{}, @@ -306,6 +309,7 @@ test "Notification" { try notifier.register(.page_navigate, &tc, TestClient.pageNavigate); notifier.dispatch(.page_navigate, &.{ + .req_id = 1, .timestamp = 4, .url = undefined, .opts = .{}, @@ -314,6 +318,7 @@ test "Notification" { notifier.unregisterAll(&tc); notifier.dispatch(.page_navigate, &.{ + .req_id = 1, .timestamp = 10, .url = undefined, .opts = .{}, @@ -323,21 +328,23 @@ test "Notification" { try notifier.register(.page_navigate, &tc, TestClient.pageNavigate); try notifier.register(.page_navigated, &tc, TestClient.pageNavigated); notifier.dispatch(.page_navigate, &.{ + .req_id = 1, .timestamp = 10, .url = undefined, .opts = .{}, }); - notifier.dispatch(.page_navigated, &.{ .timestamp = 6, .url = undefined, .opts = .{} }); + notifier.dispatch(.page_navigated, &.{ .req_id = 1, .timestamp = 6, .url = undefined, .opts = .{} }); try testing.expectEqual(14, tc.page_navigate); try testing.expectEqual(6, tc.page_navigated); notifier.unregisterAll(&tc); notifier.dispatch(.page_navigate, &.{ + .req_id = 1, .timestamp = 100, .url = undefined, .opts = .{}, }); - notifier.dispatch(.page_navigated, &.{ .timestamp = 100, .url = undefined, .opts = .{} }); + notifier.dispatch(.page_navigated, &.{ .req_id = 1, .timestamp = 100, .url = undefined, .opts = .{} }); try testing.expectEqual(14, tc.page_navigate); try testing.expectEqual(6, tc.page_navigated); @@ -345,27 +352,27 @@ test "Notification" { // unregister 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, .opts = .{} }); + notifier.dispatch(.page_navigate, &.{ .req_id = 1, .timestamp = 100, .url = undefined, .opts = .{} }); + notifier.dispatch(.page_navigated, &.{ .req_id = 1, .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, .opts = .{} }); + notifier.dispatch(.page_navigate, &.{ .req_id = 1, .timestamp = 100, .url = undefined, .opts = .{} }); + notifier.dispatch(.page_navigated, &.{ .req_id = 1, .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, .opts = .{} }); + notifier.dispatch(.page_navigate, &.{ .req_id = 1, .timestamp = 100, .url = undefined, .opts = .{} }); + notifier.dispatch(.page_navigated, &.{ .req_id = 1, .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, .opts = .{} }); + notifier.dispatch(.page_navigate, &.{ .req_id = 1, .timestamp = 100, .url = undefined, .opts = .{} }); + notifier.dispatch(.page_navigated, &.{ .req_id = 1, .timestamp = 1000, .url = undefined, .opts = .{} }); try testing.expectEqual(114, tc.page_navigate); try testing.expectEqual(2006, tc.page_navigated); }