From fbc71d6ff74e28c14d9fc7680418ee9f90188bd6 Mon Sep 17 00:00:00 2001 From: Pierre Tachoire Date: Sat, 21 Mar 2026 16:21:59 +0100 Subject: [PATCH] cdp: handle STARTUP session into Page.getFrameTree gracefully --- src/cdp/cdp.zig | 67 ++-------------------------------------- src/cdp/domains/page.zig | 54 +++++++++++++++++++++++++++++--- 2 files changed, 53 insertions(+), 68 deletions(-) diff --git a/src/cdp/cdp.zig b/src/cdp/cdp.zig index 1c5c113c..270503c4 100644 --- a/src/cdp/cdp.zig +++ b/src/cdp/cdp.zig @@ -189,27 +189,10 @@ pub fn CDPT(comptime TypeProvider: type) type { // (I can imagine this logic will become driver-specific) fn dispatchStartupCommand(command: anytype, method: []const u8) !void { // Stagehand parses the response and error if we don't return a - // correct one for this call. + // correct one for Page.getFrameTree on startup call. if (std.mem.eql(u8, method, "Page.getFrameTree")) { - // If we have a brower context and a target id, we can call the - // real dispatch command, even during STARTUP. - if (command.cdp.browser_context) |*bc| { - if (bc.target_id != null) { - return dispatchCommand(command, method); - } - } - - return command.sendResult(.{ - .frameTree = .{ - .frame = .{ - .id = "TID-STARTUP", - .loaderId = "LID-STARTUP", - .securityOrigin = URL_BASE, - .url = "about:blank", - .secureContextType = "Secure", - }, - }, - }, .{}); + // The Page.getFrameTree handles startup response gracefully. + return dispatchCommand(command, method); } return command.sendResult(null, .{}); @@ -995,47 +978,3 @@ test "cdp: STARTUP sessionId" { try ctx.expectSentResult(null, .{ .id = 4, .index = 0, .session_id = "STARTUP" }); } } - -test "cdp: STARTUP getFrameTree returns real frame ID when page exists" { - var ctx = testing.context(); - defer ctx.deinit(); - - { - // no browser context - should return TID-STARTUP - try ctx.processMessage(.{ .id = 1, .method = "Page.getFrameTree", .sessionId = "STARTUP" }); - try ctx.expectSentResult(.{ - .frameTree = .{ - .frame = .{ - .id = "TID-STARTUP", - .loaderId = "LID-STARTUP", - .url = "about:blank", - .secureContextType = "Secure", - }, - }, - }, .{ .id = 1, .session_id = "STARTUP" }); - } - - { - // browser context with target_id - should return real frame ID - const bc = try ctx.loadBrowserContext(.{ .target_id = "TID-000000000X".* }); - try ctx.processMessage(.{ .id = 2, .method = "Page.getFrameTree", .sessionId = "STARTUP" }); - try ctx.expectSentResult(.{ - .frameTree = .{ - .frame = .{ - .id = "TID-000000000X", - .loaderId = "LID-0000000001", - .url = "about:blank", - .domainAndRegistry = "", - .securityOrigin = bc.security_origin, - .mimeType = "text/html", - .adFrameStatus = .{ - .adFrameType = "none", - }, - .secureContextType = bc.secure_context_type, - .crossOriginIsolatedContextType = "NotIsolated", - .gatedAPIFeatures = [_][]const u8{}, - }, - }, - }, .{ .id = 2, .session_id = "STARTUP" }); - } -} diff --git a/src/cdp/domains/page.zig b/src/cdp/domains/page.zig index f40d54a8..96932810 100644 --- a/src/cdp/domains/page.zig +++ b/src/cdp/domains/page.zig @@ -75,8 +75,21 @@ const Frame = struct { }; fn getFrameTree(cmd: anytype) !void { - const bc = cmd.browser_context orelse return error.BrowserContextNotLoaded; - const target_id = bc.target_id orelse return error.TargetNotLoaded; + // Stagehand parses the response and error if we don't return a + // correct one for this call when browser context or target id are missing. + const startup = .{ + .frameTree = .{ + .frame = .{ + .id = "TID-STARTUP", + .loaderId = "LID-STARTUP", + .securityOrigin = @import("../cdp.zig").URL_BASE, + .url = "about:blank", + .secureContextType = "Secure", + }, + }, + }; + const bc = cmd.browser_context orelse return cmd.sendResult(startup, .{}); + const target_id = bc.target_id orelse return cmd.sendResult(startup, .{}); return cmd.sendResult(.{ .frameTree = .{ @@ -633,8 +646,18 @@ test "cdp.page: getFrameTree" { defer ctx.deinit(); { - try ctx.processMessage(.{ .id = 10, .method = "Page.getFrameTree", .params = .{ .targetId = "X" } }); - try ctx.expectSentError(-31998, "BrowserContextNotLoaded", .{ .id = 10 }); + // no browser context - should return TID-STARTUP + try ctx.processMessage(.{ .id = 1, .method = "Page.getFrameTree", .sessionId = "STARTUP" }); + try ctx.expectSentResult(.{ + .frameTree = .{ + .frame = .{ + .id = "TID-STARTUP", + .loaderId = "LID-STARTUP", + .url = "about:blank", + .secureContextType = "Secure", + }, + }, + }, .{ .id = 1, .session_id = "STARTUP" }); } const bc = try ctx.loadBrowserContext(.{ .id = "BID-9", .url = "hi.html", .target_id = "FID-000000000X".* }); @@ -659,6 +682,29 @@ test "cdp.page: getFrameTree" { }, }, .{ .id = 11 }); } + + { + // STARTUP sesion is handled when a broweser context and a target id exists. + try ctx.processMessage(.{ .id = 12, .method = "Page.getFrameTree", .session_id = "STARTUP" }); + try ctx.expectSentResult(.{ + .frameTree = .{ + .frame = .{ + .id = "FID-000000000X", + .loaderId = "LID-0000000001", + .url = "http://127.0.0.1:9582/src/browser/tests/hi.html", + .domainAndRegistry = "", + .securityOrigin = bc.security_origin, + .mimeType = "text/html", + .adFrameStatus = .{ + .adFrameType = "none", + }, + .secureContextType = bc.secure_context_type, + .crossOriginIsolatedContextType = "NotIsolated", + .gatedAPIFeatures = [_][]const u8{}, + }, + }, + }, .{ .id = 12 }); + } } test "cdp.page: captureScreenshot" {