[fix]: adopt pre-existing and nested cross-origin iframes

FO
6d ago
+307-30

why

BrowserContext only adopts an OOPIF session when its main frame is already in a page's frame graph, or when the parent session later emits Page.frameAttached for it. Two cases never get that event, so the child session stays in pendingOopifByMainFrame forever and snapshots/act can't see inside the iframe:

  1. Bootstrapping against tabs that already contain a loaded cross-origin iframe (reconnect, or an extension service-worker restart): the parent's Page.getFrameTree omits remote frames, and no new frameAttached fires.
  2. An OOPIF nested inside another OOPIF.

Two related gaps:

  • A parent Page.frameAttached that arrives after the OOPIF was adopted gives ownership back to the parent session (FrameRegistry.onFrameAttached trusts the emitter).
  • A frameAttached that adds the frame a pending OOPIF is waiting on (e.g. a same-site wrapper frame) didn't retry the other pending OOPIFs.

Repro: open a page with a cross-origin iframe, then start or reconnect Stagehand against that tab. The iframe's frame id never appears in the page's frame tree, and its contents are missing from snapshots.

what changed

  • The child-attach path falls back to the page that owns the child root's parentId, taken from the child's own Page.getFrameTree. If that page isn't known yet, the parent id is recorded, and the child is adopted once the page registers, a parent OOPIF is adopted, or a frameAttached reports the parent frame. This also covers nesting.
  • Adopted OOPIF sessions are tracked per frame, so a later parent frameAttached no longer takes ownership while the child session is alive.
  • Consolidated the owner lookup into findPageOwningFrame and the adoption steps into adoptOopif.
  • Regenerated the embedded Go extension asset (packages/extension/artifacts/stagehand-extension.zip → sdk-go/internal/extensionassets; manifest version 1.1.1 matches). Go isn't installed locally, so I copied the zip by hand instead of running extensionpack. Please let CI's extensionpack --check confirm it.

test plan

  • New packages/extension/tests/understudy-context-oopif-adoption.test.ts drives the real BrowserContext + CdpConnection over a scripted fake transport. Cases: pre-existing iframe; iframe attaching before its page registers; late parent frameAttached keeps the child owner; nested OOPIF; waiting OOPIFs adopted once their wrapper frame is reported. Each fails on main without its part of the fix.
  • The same logic was validated against real headless Chrome as a patch on 3.5.0.
  • pnpm check (43/43) and extension unit tests (1008 passed). Go and Python checks weren't run locally.

šŸ¤– Generated with Claude Code


Summary by cubic

Fixes adoption of cross-origin iframes that were already loaded when Stagehand attached (reconnect, extension restarts) or that are nested inside another cross-origin iframe, so snapshots and actions can see inside them.

Bug Fixes

  • The child attach path now falls back to the page that owns the child's parent frame, and retries all pending OOPIFs once that page registers or reports the frame.
  • A late parent Page.frameAttached no longer takes ownership of an already-adopted OOPIF session.
  • Tests drive the real BrowserContext over a scripted fake transport; each case fails on main.
  • The embedded Go extension asset was regenerated, including a merge from main that resolved its zip; CI's extensionpack --check should verify it.