Skip to content

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

Open
ForrestMoret-at wants to merge 5 commits into
browserbase:mainfrom
ForrestMoret-at:fix/adopt-preexisting-oopifs
Open

ForrestMoret-at wants to merge 5 commits into
browserbase:mainfrom
ForrestMoret-at:fix/adopt-preexisting-oopifs

Conversation

@ForrestMoret-at

@ForrestMoret-at ForrestMoret-at commented Oct 19, 2026 •

Copy link
Copy Markdown

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.

Written for commit dc0cd99. Summary will update on new commits.

View guided diff

ForrestMoret-at and others added 3 commits October 19, 2026 12:01
An OOPIF session was only adopted when its main frame was already in a
page's frame graph, or when the parent session later emitted
Page.frameAttached for it. Frames that were already loaded when the
context bootstrapped (reconnect, service-worker restart) and OOPIFs
nested inside another OOPIF never get that event, so they stayed pending
and snapshots/act could not see inside them.

- fall back to the page owning the child's parentId, and adopt pending
  children once that parent page registers
- keep an adopted OOPIF session as owner when the parent session reports
  the frame late

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A Page.frameAttached can add the frame a pending OOPIF is waiting on (for
example a same-site wrapper frame the page had not reported yet) without
re-checking the other pending OOPIFs, leaving them unadopted. Retry the
cascading adoption after every frameAttached, route that path through
adoptOopif, and resync the embedded Go extension asset.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@changeset-bot

changeset-bot Bot commented Oct 19, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: dc0cd99

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 24 packages
Name Type
@browserbasehq/stagehand-extension Patch
@browserbasehq/stagehand Patch
@browserbasehq/stagehand-python Patch
@browserbasehq/stagehand-go Patch
browse Patch
@browserbasehq/stagehand-examples Patch
@browserbasehq/stagehand-integrations-claude-cua-sdk Patch
@browserbasehq/stagehand-integrations Patch
@browserbasehq/eve Patch
@browserbasehq/stagehand-integrations-gemini-cua-sdk Patch
@browserbasehq/stagehand-integrations-example-pi-facade Patch
@browserbasehq/stagehand-integrations-claude-agent-sdk Patch
@browserbasehq/stagehand-integrations-example-claude-code-facade Patch
@browserbasehq/stagehand-integrations-codex-sdk Patch
@browserbasehq/stagehand-integrations-example-codex-facade Patch
@browserbasehq/stagehand-integrations-cursor-sdk Patch
@browserbasehq/stagehand-integrations-example-cursor-facade Patch
@browserbasehq/stagehand-integrations-deepagents-sdk Patch
@browserbasehq/stagehand-integrations-eve-sdk Patch
@browserbasehq/stagehand-integrations-fx-sdk Patch
@browserbasehq/stagehand-integrations-mastra-sdk Patch
@browserbasehq/stagehand-integrations-example-mastra-facade Patch
@browserbasehq/stagehand-integrations-pi-sdk Patch
@browserbasehq/stagehand-integrations-example-vercel-ai-facade Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actions

Copy link
Copy Markdown
Contributor

This PR is from an external contributor and must be approved by a stagehand team member with write access before CI can run.
Approving the latest commit mirrors it into an internal PR owned by the approver.
If new commits are pushed later, the internal PR stays open but is marked stale until someone approves the latest external commit and refreshes it.

@github-actions github-actions Bot added external-contributor Tracks PRs mirrored from external contributor forks. external-contributor:awaiting-approval Waiting for a stagehand team member to approve the latest external commit. labels Oct 19, 2026
…-oopifs

# Conflicts:
#	packages/sdk-go/internal/extensionassets/stagehand-extension.zip
…-oopifs

# Conflicts:
#	packages/sdk-go/internal/extensionassets/stagehand-extension.zip

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

external-contributor:awaiting-approval Waiting for a stagehand team member to approve the latest external commit. external-contributor Tracks PRs mirrored from external contributor forks.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant