fix(cdp): return real frame ID in STARTUP getFrameTree when page exists

dispatchStartupCommand hard-codes "TID-STARTUP" as the frame ID in
Page.getFrameTree. When a driver connects via connectOverCDP after a
real page already exists, subsequent lifecycle events (frameNavigated)
use the actual page frame ID. The driver's frame tracking was
initialized with "TID-STARTUP", causing a mismatch that hangs
navigation.

Check for an existing browser context with a target_id in
dispatchStartupCommand. If present, return the real frame ID and URL.
Fall back to "TID-STARTUP" only when no page exists yet.

Fixes #1800

This contribution was developed with AI assistance (Claude Code + Codex).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Matt Van Horn
2026-03-19 16:45:17 -07:00
parent 4cdc24326a
commit 6925fc3f70

View File

@@ -191,6 +191,25 @@ pub fn CDPT(comptime TypeProvider: type) type {
// Stagehand parses the response and error if we don't return a
// correct one for this call.
if (std.mem.eql(u8, method, "Page.getFrameTree")) {
// If a real page already exists (connectOverCDP flow), return
// its actual frame ID so that subsequent lifecycle events
// (frameNavigated, etc.) match the driver's frame tracking.
if (command.cdp.browser_context) |*bc| {
if (bc.target_id) |*target_id| {
return command.sendResult(.{
.frameTree = .{
.frame = .{
.id = target_id,
.loaderId = "LOADERID24DD2FD56CF1EF33C965C79C",
.securityOrigin = URL_BASE,
.url = bc.getURL() orelse "about:blank",
.secureContextType = "Secure",
},
},
}, .{});
}
}
// No real page yet - return the synthetic STARTUP frame ID.
return command.sendResult(.{
.frameTree = .{
.frame = .{
@@ -987,3 +1006,39 @@ 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 = "LOADERID24DD2FD56CF1EF33C965C79C",
.url = "about:blank",
.secureContextType = "Secure",
},
},
}, .{ .id = 1, .session_id = "STARTUP" });
}
{
// browser context with target_id - should return real frame ID
_ = 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 = "LOADERID24DD2FD56CF1EF33C965C79C",
.url = "about:blank",
.secureContextType = "Secure",
},
},
}, .{ .id = 2, .session_id = "STARTUP" });
}
}