@@ -117,7 +117,7 @@ class TargetRegistry {
117
117
const target = this . _browserToTarget . get ( browser ) ;
118
118
if ( ! target )
119
119
return ;
120
- target . emit ( 'crashed' ) ;
120
+ target . emit ( PageTarget . Events . Crashed ) ;
121
121
target . dispose ( ) ;
122
122
}
123
123
} , 'oop-frameloader-crashed' ) ;
@@ -157,6 +157,8 @@ class TargetRegistry {
157
157
target . updateUserAgent ( ) ;
158
158
if ( ! hasExplicitSize )
159
159
target . updateViewportSize ( ) ;
160
+ if ( browserContext . screencastOptions )
161
+ target . _startVideoRecording ( browserContext . screencastOptions ) ;
160
162
} ;
161
163
162
164
const onTabCloseListener = event => {
@@ -329,13 +331,20 @@ class PageTarget {
329
331
this . _openerId = opener ? opener . id ( ) : undefined ;
330
332
this . _channel = SimpleChannel . createForMessageManager ( `browser::page[${ this . _targetId } ]` , this . _linkedBrowser . messageManager ) ;
331
333
this . _screencastInfo = undefined ;
334
+ this . _dialogs = new Map ( ) ;
332
335
333
336
const navigationListener = {
334
337
QueryInterface : ChromeUtils . generateQI ( [ Ci . nsIWebProgressListener , Ci . nsISupportsWeakReference ] ) ,
335
338
onLocationChange : ( aWebProgress , aRequest , aLocation ) => this . _onNavigated ( aLocation ) ,
336
339
} ;
337
340
this . _eventListeners = [
338
341
helper . addProgressListener ( tab . linkedBrowser , navigationListener , Ci . nsIWebProgress . NOTIFY_LOCATION ) ,
342
+ helper . addEventListener ( this . _linkedBrowser , 'DOMWillOpenModalDialog' , async ( event ) => {
343
+ // wait for the dialog to be actually added to DOM.
344
+ await Promise . resolve ( ) ;
345
+ this . _updateModalDialogs ( ) ;
346
+ } ) ,
347
+ helper . addEventListener ( this . _linkedBrowser , 'DOMModalDialogClosed' , event => this . _updateModalDialogs ( ) ) ,
339
348
] ;
340
349
341
350
this . _disposed = false ;
@@ -346,6 +355,14 @@ class PageTarget {
346
355
this . _registry . emit ( TargetRegistry . Events . TargetCreated , this ) ;
347
356
}
348
357
358
+ dialog ( dialogId ) {
359
+ return this . _dialogs . get ( dialogId ) ;
360
+ }
361
+
362
+ dialogs ( ) {
363
+ return [ ...this . _dialogs . values ( ) ] ;
364
+ }
365
+
349
366
async windowReady ( ) {
350
367
await waitForWindowReady ( this . _window ) ;
351
368
}
@@ -362,6 +379,25 @@ class PageTarget {
362
379
this . _linkedBrowser . browsingContext . customUserAgent = this . _browserContext . defaultUserAgent ;
363
380
}
364
381
382
+ _updateModalDialogs ( ) {
383
+ const prompts = new Set ( this . _linkedBrowser . tabModalPromptBox ? this . _linkedBrowser . tabModalPromptBox . listPrompts ( ) : [ ] ) ;
384
+ for ( const dialog of this . _dialogs . values ( ) ) {
385
+ if ( ! prompts . has ( dialog . prompt ( ) ) ) {
386
+ this . _dialogs . delete ( dialog . id ( ) ) ;
387
+ this . emit ( PageTarget . Events . DialogClosed , dialog ) ;
388
+ } else {
389
+ prompts . delete ( dialog . prompt ( ) ) ;
390
+ }
391
+ }
392
+ for ( const prompt of prompts ) {
393
+ const dialog = Dialog . createIfSupported ( prompt ) ;
394
+ if ( ! dialog )
395
+ continue ;
396
+ this . _dialogs . set ( dialog . id ( ) , dialog ) ;
397
+ this . emit ( PageTarget . Events . DialogOpened , dialog ) ;
398
+ }
399
+ }
400
+
365
401
async updateViewportSize ( ) {
366
402
// Viewport size is defined by three arguments:
367
403
// 1. default size. Could be explicit if set as part of `window.open` call, e.g.
@@ -433,7 +469,7 @@ class PageTarget {
433
469
return await this . _channel . connect ( '' ) . send ( 'hasFailedToOverrideTimezone' ) . catch ( e => true ) ;
434
470
}
435
471
436
- async startVideoRecording ( { width, height, scale, dir} ) {
472
+ async _startVideoRecording ( { width, height, scale, dir} ) {
437
473
// On Mac the window may not yet be visible when TargetCreated and its
438
474
// NSWindow.windowNumber may be -1, so we wait until the window is known
439
475
// to be initialized and visible.
@@ -451,10 +487,10 @@ class PageTarget {
451
487
const devicePixelRatio = this . _window . devicePixelRatio ;
452
488
const videoSessionId = screencast . startVideoRecording ( docShell , file , width , height , scale || 0 , devicePixelRatio * rect . top ) ;
453
489
this . _screencastInfo = { videoSessionId, file } ;
454
- this . emit ( 'screencastStarted' ) ;
490
+ this . emit ( PageTarget . Events . ScreencastStarted ) ;
455
491
}
456
492
457
- async stopVideoRecording ( ) {
493
+ async _stopVideoRecording ( ) {
458
494
if ( ! this . _screencastInfo )
459
495
throw new Error ( 'No video recording in progress' ) ;
460
496
const screencastInfo = this . _screencastInfo ;
@@ -479,6 +515,8 @@ class PageTarget {
479
515
480
516
dispose ( ) {
481
517
this . _disposed = true ;
518
+ if ( this . _screencastInfo )
519
+ this . _stopVideoRecording ( ) . catch ( e => dump ( `stopVideoRecording failed:\n${ e } \n` ) ) ;
482
520
this . _browserContext . pages . delete ( this ) ;
483
521
this . _registry . _browserToTarget . delete ( this . _linkedBrowser ) ;
484
522
this . _registry . _browserBrowsingContextToTarget . delete ( this . _linkedBrowser . browsingContext ) ;
@@ -487,6 +525,13 @@ class PageTarget {
487
525
}
488
526
}
489
527
528
+ PageTarget . Events = {
529
+ ScreencastStarted : Symbol ( 'PageTarget.ScreencastStarted' ) ,
530
+ Crashed : Symbol ( 'PageTarget.Crashed' ) ,
531
+ DialogOpened : Symbol ( 'PageTarget.DialogOpened' ) ,
532
+ DialogClosed : Symbol ( 'PageTarget.DialogClosed' ) ,
533
+ } ;
534
+
490
535
class BrowserContext {
491
536
constructor ( registry , browserContextId , removeOnDetach ) {
492
537
this . _registry = registry ;
@@ -702,11 +747,67 @@ class BrowserContext {
702
747
return ;
703
748
const promises = [ ] ;
704
749
for ( const page of this . pages )
705
- promises . push ( page . startVideoRecording ( options ) ) ;
750
+ promises . push ( page . _startVideoRecording ( options ) ) ;
706
751
await Promise . all ( promises ) ;
707
752
}
708
753
}
709
754
755
+ class Dialog {
756
+ static createIfSupported ( prompt ) {
757
+ const type = prompt . args . promptType ;
758
+ switch ( type ) {
759
+ case 'alert' :
760
+ case 'prompt' :
761
+ case 'confirm' :
762
+ return new Dialog ( prompt , type ) ;
763
+ case 'confirmEx' :
764
+ return new Dialog ( prompt , 'beforeunload' ) ;
765
+ default :
766
+ return null ;
767
+ } ;
768
+ }
769
+
770
+ constructor ( prompt , type ) {
771
+ this . _id = helper . generateId ( ) ;
772
+ this . _type = type ;
773
+ this . _prompt = prompt ;
774
+ }
775
+
776
+ id ( ) {
777
+ return this . _id ;
778
+ }
779
+
780
+ message ( ) {
781
+ return this . _prompt . ui . infoBody . textContent ;
782
+ }
783
+
784
+ type ( ) {
785
+ return this . _type ;
786
+ }
787
+
788
+ prompt ( ) {
789
+ return this . _prompt ;
790
+ }
791
+
792
+ dismiss ( ) {
793
+ if ( this . _prompt . ui . button1 )
794
+ this . _prompt . ui . button1 . click ( ) ;
795
+ else
796
+ this . _prompt . ui . button0 . click ( ) ;
797
+ }
798
+
799
+ defaultValue ( ) {
800
+ return this . _prompt . ui . loginTextbox . value ;
801
+ }
802
+
803
+ accept ( promptValue ) {
804
+ if ( typeof promptValue === 'string' && this . _type === 'prompt' )
805
+ this . _prompt . ui . loginTextbox . value = promptValue ;
806
+ this . _prompt . ui . button0 . click ( ) ;
807
+ }
808
+ }
809
+
810
+
710
811
function dirPath ( path ) {
711
812
return path . substring ( 0 , path . lastIndexOf ( '/' ) + 1 ) ;
712
813
}
@@ -755,5 +856,6 @@ TargetRegistry.Events = {
755
856
DownloadFinished : Symbol ( 'TargetRegistry.Events.DownloadFinished' ) ,
756
857
} ;
757
858
758
- var EXPORTED_SYMBOLS = [ 'TargetRegistry' ] ;
859
+ var EXPORTED_SYMBOLS = [ 'TargetRegistry' , 'PageTarget' ] ;
759
860
this . TargetRegistry = TargetRegistry ;
861
+ this . PageTarget = PageTarget ;
0 commit comments