@@ -133,12 +133,15 @@ export class V3Context {
133133 }
134134 }
135135
136- private async ensurePiercer ( session : CDPSessionLike ) : Promise < void > {
136+ private async ensurePiercer ( session : CDPSessionLike ) : Promise < boolean > {
137137 const key = this . sessionKey ( session ) ;
138- if ( this . _piercerInstalled . has ( key ) ) return ;
138+ if ( this . _piercerInstalled . has ( key ) ) return true ;
139139
140- await installV3PiercerIntoSession ( session ) ;
141- this . _piercerInstalled . add ( key ) ;
140+ const installed = await installV3PiercerIntoSession ( session ) ;
141+ if ( installed ) {
142+ this . _piercerInstalled . add ( key ) ;
143+ }
144+ return installed ;
142145 }
143146
144147 /** Mark a page target as the most-recent one (active). */
@@ -286,11 +289,7 @@ export class V3Context {
286289 this . conn . on < Protocol . Target . AttachedToTargetEvent > (
287290 "Target.attachedToTarget" ,
288291 async ( evt ) => {
289- await this . onAttachedToTarget (
290- evt . targetInfo ,
291- evt . sessionId ,
292- evt . waitingForDebugger === true ,
293- ) ;
292+ await this . onAttachedToTarget ( evt . targetInfo , evt . sessionId ) ;
294293 } ,
295294 ) ;
296295
@@ -365,38 +364,25 @@ export class V3Context {
365364 private async onAttachedToTarget (
366365 info : Protocol . Target . TargetInfo ,
367366 sessionId : SessionId ,
368- waitingForDebugger ?: boolean , // Chrome paused this target at doc-start?
369367 ) : Promise < void > {
370368 const session = this . conn . getSession ( sessionId ) ;
371369 if ( ! session ) return ;
372370
373371 // Init guard
374372 if ( this . _sessionInit . has ( sessionId ) ) return ;
375373 this . _sessionInit . add ( sessionId ) ;
374+ await session . send ( "Runtime.runIfWaitingForDebugger" ) . catch ( ( ) => { } ) ;
375+
376+ // Register for Runtime events before enabling it so we don't miss initial contexts.
377+ executionContexts . attachSession ( session ) ;
378+
379+ const piercerReady = await this . ensurePiercer ( session ) ;
380+ if ( ! piercerReady ) return ;
376381
377- // Page domain first so frame events flow
378- const pageEnabled = await session
379- . send ( "Page.enable" )
380- . then ( ( ) => true )
381- . catch ( ( ) => false ) ;
382- if ( ! pageEnabled ) {
383- if ( waitingForDebugger ) {
384- await session . send ( "Runtime.runIfWaitingForDebugger" ) . catch ( ( ) => { } ) ;
385- }
386- return ;
387- }
388382 await session
389383 . send ( "Page.setLifecycleEventsEnabled" , { enabled : true } )
390384 . catch ( ( ) => { } ) ;
391385
392- // Proactively resume ASAP so navigation isn't stuck at about:blank
393- await session . send ( "Runtime.runIfWaitingForDebugger" ) . catch ( ( ) => { } ) ;
394-
395- // Capture main-world creations & inject piercer after resume
396- executionContexts . attachSession ( session ) ;
397- await session . send ( "Runtime.enable" ) . catch ( ( ) => { } ) ;
398- await this . ensurePiercer ( session ) ;
399-
400386 // Top-level handling
401387 if ( isTopLevelPage ( info ) ) {
402388 const page = await Page . create (
@@ -422,10 +408,6 @@ export class V3Context {
422408 this . _pushActive ( info . targetId ) ;
423409 this . installFrameEventBridges ( sessionId , page ) ;
424410
425- // Resume only if Chrome actually paused
426- if ( waitingForDebugger ) {
427- await session . send ( "Runtime.runIfWaitingForDebugger" ) . catch ( ( ) => { } ) ;
428- }
429411 return ;
430412 }
431413
@@ -458,73 +440,18 @@ export class V3Context {
458440 owner . adoptOopifSession ( session , childMainId ) ;
459441 this . sessionOwnerPage . set ( sessionId , owner ) ;
460442 this . installFrameEventBridges ( sessionId , owner ) ;
443+ // Prime the execution-context registry so later lookups succeed even if
444+ // the frame navigates before we issue a command.
445+ void executionContexts
446+ . waitForMainWorld ( session , childMainId )
447+ . catch ( ( ) => { } ) ;
461448 } else {
462449 this . pendingOopifByMainFrame . set ( childMainId , sessionId ) ;
463450 }
464451 } catch {
465452 // page.getFrameTree failed. Most likely was an ad iframe
466453 // that opened & closed before we could attach. ignore
467454 }
468-
469- /* TODO: for child OOPIFs, we rely on reloads for our script to be applied.
470- * This is not ideal. We should figure out a way to pause them, and inject them
471- * from the beginning.
472- */
473- // --- If Chrome did NOT pause this iframe (waitingForDebugger=false),
474- // we missed doc-start. Do a one-time, best-effort child reload
475- // so Page.addScriptToEvaluateOnNewDocument applies at creation time.
476- if ( info . type === "iframe" && ! waitingForDebugger ) {
477- try {
478- // Ensure lifecycle events are enabled to observe load
479- await session
480- . send ( "Page.setLifecycleEventsEnabled" , { enabled : true } )
481- . catch ( ( ) => { } ) ;
482-
483- const loadWait = new Promise < void > ( ( resolve ) => {
484- const handler = ( evt : Protocol . Page . LifecycleEventEvent ) => {
485- if ( evt . name === "load" ) {
486- session . off ( "Page.lifecycleEvent" , handler ) ;
487- resolve ( ) ;
488- }
489- } ;
490- session . on ( "Page.lifecycleEvent" , handler ) ;
491- } ) ;
492-
493- // Cannot use Page.reload on child targets → use Runtime.evaluate
494- await session
495- . send < Protocol . Runtime . EvaluateResponse > ( "Runtime.evaluate" , {
496- expression : "location.reload()" ,
497- returnByValue : true ,
498- awaitPromise : false ,
499- } )
500- . catch ( ( ) => { } ) ;
501-
502- // Wait up to ~3s for the child to finish loading (best effort)
503- await Promise . race ( [
504- loadWait ,
505- new Promise < void > ( ( r ) => setTimeout ( r , 3000 ) ) ,
506- ] ) ;
507-
508- // After reload, our addScriptToEvaluateOnNewDocument runs at doc-start.
509- // We can optionally re-evaluate the piercer (idempotent), but not required.
510- await this . ensurePiercer ( session ) ;
511- } catch ( e ) {
512- v3Logger ( {
513- category : "ctx" ,
514- message : "child reload attempt failed (continuing)" ,
515- level : 2 ,
516- auxiliary : {
517- sessionId : { value : String ( sessionId ) , type : "string" } ,
518- err : { value : String ( e ) , type : "string" } ,
519- } ,
520- } ) ;
521- }
522- }
523-
524- // Resume only if Chrome actually paused (rare for iframes in your logs)
525- if ( waitingForDebugger ) {
526- await session . send ( "Runtime.runIfWaitingForDebugger" ) . catch ( ( ) => { } ) ;
527- }
528455 }
529456
530457 /**
0 commit comments