@@ -158,11 +158,11 @@ export class FrameManager {
158
158
return ;
159
159
for ( const barrier of this . _signalBarriers )
160
160
barrier . addFrameNavigation ( frame ) ;
161
- if ( frame . _pendingDocument && frame . _pendingDocument . documentId === documentId ) {
161
+ if ( frame . pendingDocument ( ) && frame . pendingDocument ( ) ! . documentId === documentId ) {
162
162
// Do not override request with undefined.
163
163
return ;
164
164
}
165
- frame . _pendingDocument = { documentId, request : undefined } ;
165
+ frame . setPendingDocument ( { documentId, request : undefined } ) ;
166
166
}
167
167
168
168
frameCommittedNewDocumentNavigation ( frameId : string , url : string , name : string , documentId : string , initial : boolean ) {
@@ -173,24 +173,25 @@ export class FrameManager {
173
173
frame . _name = name ;
174
174
175
175
let keepPending : DocumentInfo | undefined ;
176
- if ( frame . _pendingDocument ) {
177
- if ( frame . _pendingDocument . documentId === undefined ) {
176
+ const pendingDocument = frame . pendingDocument ( ) ;
177
+ if ( pendingDocument ) {
178
+ if ( pendingDocument . documentId === undefined ) {
178
179
// Pending with unknown documentId - assume it is the one being committed.
179
- frame . _pendingDocument . documentId = documentId ;
180
+ pendingDocument . documentId = documentId ;
180
181
}
181
- if ( frame . _pendingDocument . documentId === documentId ) {
182
+ if ( pendingDocument . documentId === documentId ) {
182
183
// Committing a pending document.
183
- frame . _currentDocument = frame . _pendingDocument ;
184
+ frame . _currentDocument = pendingDocument ;
184
185
} else {
185
186
// Sometimes, we already have a new pending when the old one commits.
186
187
// An example would be Chromium error page followed by a new navigation request,
187
188
// where the error page commit arrives after Network.requestWillBeSent for the
188
189
// new navigation.
189
190
// We commit, but keep the pending request since it's not done yet.
190
- keepPending = frame . _pendingDocument ;
191
+ keepPending = pendingDocument ;
191
192
frame . _currentDocument = { documentId, request : undefined } ;
192
193
}
193
- frame . _pendingDocument = undefined ;
194
+ frame . setPendingDocument ( undefined ) ;
194
195
} else {
195
196
// No pending - just commit a new document.
196
197
frame . _currentDocument = { documentId, request : undefined } ;
@@ -205,7 +206,7 @@ export class FrameManager {
205
206
this . _page . frameNavigatedToNewDocument ( frame ) ;
206
207
}
207
208
// Restore pending if any - see comments above about keepPending.
208
- frame . _pendingDocument = keepPending ;
209
+ frame . setPendingDocument ( keepPending ) ;
209
210
}
210
211
211
212
frameCommittedSameDocumentNavigation ( frameId : string , url : string ) {
@@ -220,17 +221,17 @@ export class FrameManager {
220
221
221
222
frameAbortedNavigation ( frameId : string , errorText : string , documentId ?: string ) {
222
223
const frame = this . _frames . get ( frameId ) ;
223
- if ( ! frame || ! frame . _pendingDocument )
224
+ if ( ! frame || ! frame . pendingDocument ( ) )
224
225
return ;
225
- if ( documentId !== undefined && frame . _pendingDocument . documentId !== documentId )
226
+ if ( documentId !== undefined && frame . pendingDocument ( ) ! . documentId !== documentId )
226
227
return ;
227
228
const navigationEvent : NavigationEvent = {
228
229
url : frame . _url ,
229
230
name : frame . _name ,
230
- newDocument : frame . _pendingDocument ,
231
+ newDocument : frame . pendingDocument ( ) ,
231
232
error : new Error ( errorText ) ,
232
233
} ;
233
- frame . _pendingDocument = undefined ;
234
+ frame . setPendingDocument ( undefined ) ;
234
235
frame . emit ( Frame . Events . Navigation , navigationEvent ) ;
235
236
}
236
237
@@ -255,7 +256,7 @@ export class FrameManager {
255
256
const frame = request . frame ( ) ;
256
257
this . _inflightRequestStarted ( request ) ;
257
258
if ( request . _documentId )
258
- frame . _pendingDocument = { documentId : request . _documentId , request } ;
259
+ frame . setPendingDocument ( { documentId : request . _documentId , request } ) ;
259
260
if ( request . _isFavicon ) {
260
261
const route = request . _route ( ) ;
261
262
if ( route )
@@ -281,11 +282,11 @@ export class FrameManager {
281
282
requestFailed ( request : network . Request , canceled : boolean ) {
282
283
const frame = request . frame ( ) ;
283
284
this . _inflightRequestFinished ( request ) ;
284
- if ( frame . _pendingDocument && frame . _pendingDocument . request === request ) {
285
+ if ( frame . pendingDocument ( ) && frame . pendingDocument ( ) ! . request === request ) {
285
286
let errorText = request . failure ( ) ! . errorText ;
286
287
if ( canceled )
287
288
errorText += '; maybe frame was detached?' ;
288
- this . frameAbortedNavigation ( frame . _id , errorText , frame . _pendingDocument . documentId ) ;
289
+ this . frameAbortedNavigation ( frame . _id , errorText , frame . pendingDocument ( ) ! . documentId ) ;
289
290
}
290
291
if ( ! request . _isFavicon )
291
292
this . _page . emit ( Page . Events . RequestFailed , request ) ;
@@ -399,7 +400,7 @@ export class Frame extends SdkObject {
399
400
private _firedLifecycleEvents = new Set < types . LifecycleEvent > ( ) ;
400
401
_subtreeLifecycleEvents = new Set < types . LifecycleEvent > ( ) ;
401
402
_currentDocument : DocumentInfo ;
402
- _pendingDocument ? : DocumentInfo ;
403
+ private _pendingDocument : DocumentInfo | undefined ;
403
404
readonly _page : Page ;
404
405
private _parentFrame : Frame | null ;
405
406
_url = '' ;
@@ -412,6 +413,7 @@ export class Frame extends SdkObject {
412
413
private _setContentCounter = 0 ;
413
414
readonly _detachedPromise : Promise < void > ;
414
415
private _detachedCallback = ( ) => { } ;
416
+ private _nonStallingEvaluations = new Set < ( error : Error ) => void > ( ) ;
415
417
416
418
constructor ( page : Page , id : string , parentFrame : Frame | null ) {
417
419
super ( page , 'frame' ) ;
@@ -451,6 +453,44 @@ export class Frame extends SdkObject {
451
453
this . _startNetworkIdleTimer ( ) ;
452
454
}
453
455
456
+ setPendingDocument ( documentInfo : DocumentInfo | undefined ) {
457
+ this . _pendingDocument = documentInfo ;
458
+ if ( documentInfo )
459
+ this . _invalidateNonStallingEvaluations ( ) ;
460
+ }
461
+
462
+ pendingDocument ( ) : DocumentInfo | undefined {
463
+ return this . _pendingDocument ;
464
+ }
465
+
466
+ private async _invalidateNonStallingEvaluations ( ) {
467
+ if ( ! this . _nonStallingEvaluations )
468
+ return ;
469
+ const error = new Error ( 'Navigation interrupted the evaluation' ) ;
470
+ for ( const callback of this . _nonStallingEvaluations )
471
+ callback ( error ) ;
472
+ }
473
+
474
+ async nonStallingRawEvaluateInExistingMainContext ( expression : string ) : Promise < any > {
475
+ if ( this . _pendingDocument )
476
+ throw new Error ( 'Frame is currently attempting a navigation' ) ;
477
+ const context = this . _existingMainContext ( ) ;
478
+ if ( ! context )
479
+ throw new Error ( 'Frame does not yet have a main execution context' ) ;
480
+
481
+ let callback = ( ) => { } ;
482
+ const frameInvalidated = new Promise < void > ( ( f , r ) => callback = r ) ;
483
+ this . _nonStallingEvaluations . add ( callback ) ;
484
+ try {
485
+ return await Promise . race ( [
486
+ context . rawEvaluateJSON ( expression ) ,
487
+ frameInvalidated
488
+ ] ) ;
489
+ } finally {
490
+ this . _nonStallingEvaluations . delete ( callback ) ;
491
+ }
492
+ }
493
+
454
494
private _recalculateLifecycle ( ) {
455
495
const events = new Set < types . LifecycleEvent > ( this . _firedLifecycleEvents ) ;
456
496
for ( const child of this . _childFrames ) {
@@ -584,7 +624,7 @@ export class Frame extends SdkObject {
584
624
return this . _context ( 'main' ) ;
585
625
}
586
626
587
- _existingMainContext ( ) : dom . FrameExecutionContext | null {
627
+ private _existingMainContext ( ) : dom . FrameExecutionContext | null {
588
628
return this . _contextData . get ( 'main' ) ?. context || null ;
589
629
}
590
630
0 commit comments