Skip to content

[fix]: ctx.addInitScript() iframe issues#1664

Draft
seanmcguire12 wants to merge 4 commits intomainfrom
fix-ctx-add-init-script-iframes
Draft

[fix]: ctx.addInitScript() iframe issues#1664
seanmcguire12 wants to merge 4 commits intomainfrom
fix-ctx-add-init-script-iframes

Conversation

@seanmcguire12
Copy link
Member

@seanmcguire12 seanmcguire12 commented Feb 5, 2026

why

  • scripts added viacontext.addInitScript() were not being applied in the following scenarios:
    • for pages with OOPIFs, the script was not being applied to the iframe
    • for pages with SPIFs, the script was not being applied to the iframe if the page was opened via a popup

what changed

  • added runImmediately: true to Page.addScriptToEvaluateOnNewDocument calls to so that scripts execute before any other code runs
  • refactored target attachment to propagate Target.setAutoAttach with waitForDebuggerOnStart: true to nested sessions, ensuring OOPIFs are paused before they can execute
  • moved target resume (Runtime.runIfWaitingForDebugger) to happen after init scripts are registered to prevent init scripts being missed
  • added per-session tracking of Target.attachedToTarget listeners to properly handle nested iframe attachment chains
  • removed the explicit Target.targetCreated fallback attach logic, relying instead on the properly configured auto-attach cascade

test plan


Summary by cubic

Fixes context.addInitScript so init scripts run in OOPIFs and in popup pages with SPIF iframes. Scripts now execute before any page code and reliably propagate across nested targets.

  • Bug Fixes
    • Run init scripts immediately via Page.addScriptToEvaluateOnNewDocument.
    • Cascade Target.setAutoAttach with waitForDebuggerOnStart to nested sessions to pause OOPIFs.
    • Resume targets after registering init scripts to prevent misses.
    • Track per-session Target event listeners for nested iframe chains.
    • Remove Target.targetCreated fallback; rely on auto-attach cascade.
    • Install the piercer in all frames (getFrameTree + frameNavigated) using main-world contexts.
    • Unskip iframe tests for context.addInitScript.

Written for commit 7a9accb. Summary will update on new commits. Review in cubic

@changeset-bot
Copy link

changeset-bot bot commented Feb 5, 2026

🦋 Changeset detected

Latest commit: 7a9accb

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

This PR includes changesets to release 3 packages
Name Type
@browserbasehq/stagehand Patch
@browserbasehq/stagehand-evals Patch
@browserbasehq/stagehand-server 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

@seanmcguire12
Copy link
Member Author

@cubic-dev-ai @greptileai

@cubic-dev-ai
Copy link
Contributor

cubic-dev-ai bot commented Feb 5, 2026

@cubic-dev-ai @greptileai

@seanmcguire12 I have started the AI code review. It will take a few minutes to complete.

Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 6 files

Confidence score: 5/5

  • Automated review surfaced no issues in the provided summaries.
  • No files require special attention.
Architecture diagram
sequenceDiagram
    participant B as Browser / CDP
    participant C as V3Context
    participant S as CDPSession (Page/OOPIF)
    participant P as Piercer / ExecutionContext

    Note over B,S: Target Attachment & Init Script Flow

    B-->>C: Target.attachedToTarget (Target Paused)
    C->>C: NEW: installTargetSessionListeners()
    
    rect rgb(23, 37, 84)
    Note right of C: Session Initialization (onAttachedToTarget)
    C->>S: Page.enable()
    C->>S: Runtime.enable()
    
    C->>S: NEW: Target.setAutoAttach(waitForDebuggerOnStart: true, flatten: true)
    Note right of S: Ensures nested OOPIFs also pause on start
    
    opt Has Init Scripts
        C->>S: CHANGED: Page.addScriptToEvaluateOnNewDocument(source, runImmediately: true)
    end
    
    C->>S: NEW: Runtime.runIfWaitingForDebugger()
    Note right of S: Target resumes ONLY after scripts are registered
    end

    Note over C,P: Piercer Installation Flow

    C->>S: NEW: Page.getFrameTree()
    S-->>C: frameTree (All nested frames)
    
    loop Each Frame in Tree
        C->>P: NEW: waitForMainWorld(frameId)
        P-->>C: executionContextId
        C->>S: Runtime.evaluate(v3ScriptContent, contextId)
        C->>S: Runtime.evaluate(reRenderScriptContent, contextId)
    end

    B-->>C: NEW: Page.frameNavigated (Event)
    C->>P: Re-install piercer in new main world context

    alt Unhappy Path: Session Closed
        S-->>C: Error: Session with given id not found
        C->>C: Cleanup session listeners & tracking
    end
Loading

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Feb 5, 2026

Greptile Overview

Greptile Summary

Fixed context.addInitScript() to properly apply scripts in OOPIFs and SPIFs by adding runImmediately: true to script registration, propagating Target.setAutoAttach with waitForDebuggerOnStart: true to nested sessions, and ensuring init scripts are registered before targets resume.

Key changes:

  • Added per-session tracking of target attachment listeners to handle nested iframe chains
  • Refactored init script installation to set runImmediately: true on all Page.addScriptToEvaluateOnNewDocument calls
  • Moved Runtime.runIfWaitingForDebugger to execute after init scripts are registered (via CDP command ordering)
  • Removed fallback Target.targetCreated attach logic, relying on properly configured auto-attach cascade
  • Refactored piercer to use per-frame script evaluation with execution context tracking rather than document-level script registration
  • All previously failing tests now pass

Confidence Score: 4/5

  • This PR is safe to merge with minor style concerns that can be addressed in follow-up work
  • The fix correctly addresses the OOPIF/SPIF init script issue through proper target lifecycle management. The changes are well-tested with comprehensive test coverage. Minor concerns around potential memory leaks from unremoved event listeners and promise execution ordering don't affect core functionality but could be optimized.
  • Pay attention to packages/core/lib/v3/understudy/context.ts and packages/core/lib/v3/understudy/piercer.ts for the core logic changes

Important Files Changed

Filename Overview
packages/core/lib/v3/understudy/context.ts refactored target attachment to propagate auto-attach with waitForDebuggerOnStart to nested sessions, and moved init script installation to happen before target resume
packages/core/lib/v3/understudy/piercer.ts refactored to install piercer per-frame by waiting for execution contexts and evaluating scripts, rather than using addScriptToEvaluateOnNewDocument
packages/core/lib/v3/understudy/page.ts added runImmediately: true to addScriptToEvaluateOnNewDocument call

Sequence Diagram

sequenceDiagram
    participant Context as V3Context
    participant CDP as CDP Connection
    participant Browser as Browser/Target
    participant Session as CDP Session
    participant Frame as OOPIF/SPIF

    Note over Context,Browser: Target Attachment Flow (Fixed)
    
    Context->>CDP: enableAutoAttach(waitForDebuggerOnStart: true)
    Browser->>Context: Target.attachedToTarget (paused)
    
    Context->>Session: installTargetSessionListeners()
    Note over Session: Attach nested target listeners
    
    Context->>Session: Page.enable
    Context->>Session: Runtime.enable
    Context->>Session: Target.setAutoAttach(waitForDebuggerOnStart: true, flatten: true)
    Note over Context,Session: Propagate auto-attach to nested sessions
    
    loop For each init script
        Context->>Session: Page.addScriptToEvaluateOnNewDocument(runImmediately: true)
    end
    
    Context->>Session: Runtime.runIfWaitingForDebugger
    Note over Session,Browser: Resume AFTER init scripts registered
    
    Browser->>Frame: Frame created (OOPIF/SPIF)
    Frame->>Session: Target.attachedToTarget (nested, paused)
    Session->>Frame: Install init scripts (propagated)
    Session->>Frame: Runtime.runIfWaitingForDebugger
    
    Note over Frame: Init scripts execute before page load
Loading

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

3 files reviewed, 2 comments

Edit Code Review Agent Settings | Greptile

Comment on lines +46 to +50
session.on<Protocol.Page.FrameNavigatedEvent>(
"Page.frameNavigated",
(evt) => {
void installInFrame(evt.frame.id);
},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

listener not removed on session close

The Page.frameNavigated listener is added but never removed. In a long-lived session with many navigations, this could lead to memory issues if the same session is reused.

Consider adding cleanup logic to remove the listener when the session closes or when installV3PiercerIntoSession completes.

Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/core/lib/v3/understudy/piercer.ts
Line: 46:50

Comment:
listener not removed on session close

The `Page.frameNavigated` listener is added but never removed. In a long-lived session with many navigations, this could lead to memory issues if the same session is reused.

Consider adding cleanup logic to remove the listener when the session closes or when `installV3PiercerIntoSession` completes.

How can I resolve this? If you propose a fix, please make it concise.

);
}
}
installPromises.push(resume());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

resume() added to promise array without await

The resume() call is added to installPromises but this creates a race condition. If resume() executes before init scripts are registered, the scripts may miss the document load event.

The fix should ensure resume() only executes after all init script registrations have been sent to CDP. Consider wrapping the init script loop in a Promise.all() and chaining resume() after.

Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/core/lib/v3/understudy/context.ts
Line: 489:489

Comment:
`resume()` added to promise array without `await`

The `resume()` call is added to `installPromises` but this creates a race condition. If `resume()` executes before init scripts are registered, the scripts may miss the document load event.

The fix should ensure `resume()` only executes after all init script registrations have been sent to CDP. Consider wrapping the init script loop in a `Promise.all()` and chaining `resume()` after.

How can I resolve this? If you propose a fix, please make it concise.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant