@@ -140,7 +140,7 @@ class GeckodriverHttpDriver implements IDriver {
140140 this . webSocketUrl = webSocketUrl ;
141141 }
142142
143- static async connect ( marionettePort : number ) : Promise < GeckodriverHttpDriver > {
143+ static async connect ( marionettePort : number , marionetteHost = '127.0.0.1' ) : Promise < GeckodriverHttpDriver > {
144144 // Find geckodriver binary via selenium-manager
145145 const path = await import ( 'node:path' ) ;
146146 const { execFileSync } = await import ( 'node:child_process' ) ;
@@ -179,7 +179,7 @@ class GeckodriverHttpDriver implements IDriver {
179179 // Use --port=0 to let the OS assign a free port atomically (geckodriver ≥0.34.0)
180180 const gd = spawn (
181181 geckodriverPath ,
182- [ '--connect-existing' , '--marionette-port' , String ( marionettePort ) , '--port' , '0' ] ,
182+ [ '--connect-existing' , '--marionette-host' , marionetteHost , '--marionette- port', String ( marionettePort ) , '--port' , '0' ] ,
183183 { stdio : [ 'ignore' , 'pipe' , 'pipe' ] }
184184 ) ;
185185
@@ -223,7 +223,14 @@ class GeckodriverHttpDriver implements IDriver {
223223 throw new Error ( `Failed to create session: ${ JSON . stringify ( json ) } ` ) ;
224224 }
225225
226- const wsUrl = json . value . capabilities . webSocketUrl as string | undefined ;
226+ let wsUrl = json . value . capabilities . webSocketUrl as string | undefined ;
227+ logDebug ( `Session capabilities webSocketUrl: ${ wsUrl ?? 'not present' } , marionetteHost: ${ marionetteHost } ` ) ;
228+ if ( wsUrl && marionetteHost !== '127.0.0.1' ) {
229+ // Rewrite the URL to connect through the remote host / tunnel.
230+ const parsed = new URL ( wsUrl ) ;
231+ parsed . hostname = marionetteHost ;
232+ wsUrl = parsed . toString ( ) ;
233+ }
227234 if ( wsUrl ) {
228235 logDebug ( `BiDi WebSocket URL: ${ wsUrl } ` ) ;
229236 } else {
@@ -445,12 +452,18 @@ class GeckodriverHttpDriver implements IDriver {
445452 this . gdProcess . kill ( ) ;
446453 }
447454
448- /** Kill the geckodriver process without closing Firefox */
449- kill ( ) : void {
455+ /** Kill the geckodriver process without closing Firefox.
456+ * Deletes the session first so Marionette accepts new connections. */
457+ async kill ( ) : Promise < void > {
450458 if ( this . bidiConnection ) {
451459 ( this . bidiConnection . socket as unknown as WebSocket ) . close ( ) ;
452460 this . bidiConnection = null ;
453461 }
462+ try {
463+ await this . cmd ( 'DELETE' , '' ) ;
464+ } catch {
465+ // ignore
466+ }
454467 this . gdProcess . kill ( ) ;
455468 }
456469
@@ -470,9 +483,11 @@ class GeckodriverHttpDriver implements IDriver {
470483 const ws = new WebSocket ( this . webSocketUrl ) ;
471484 await new Promise < void > ( ( resolve , reject ) => {
472485 ws . on ( 'open' , resolve ) ;
473- ws . on ( 'error' , reject ) ;
486+ ws . on ( 'error' , ( e : any ) => {
487+ const msg = e ?. message || e ?. error ?. message || e ?. error || e ?. type || JSON . stringify ( e ) || String ( e ) ;
488+ reject ( new Error ( `BiDi WS to ${ this . webSocketUrl } : ${ msg } ` ) ) ;
489+ } ) ;
474490 } ) ;
475- logDebug ( 'BiDi WebSocket connected' ) ;
476491
477492 let cmdId = 0 ;
478493 const subscribe = async ( event : string , contexts ?: string [ ] ) : Promise < void > => {
@@ -572,7 +587,8 @@ export class FirefoxCore {
572587 // We bypass selenium-webdriver because its BiDi auto-upgrade hangs
573588 // when used with geckodriver's --connect-existing mode.
574589 const port = this . options . marionettePort ?? 2828 ;
575- this . driver = await GeckodriverHttpDriver . connect ( port ) ;
590+ const host = this . options . marionetteHost ?? '127.0.0.1' ;
591+ this . driver = await GeckodriverHttpDriver . connect ( port , host ) ;
576592 } else {
577593 // Set up output file for capturing Firefox stdout/stderr
578594 if ( this . options . logFile ) {
@@ -709,7 +725,7 @@ export class FirefoxCore {
709725 */
710726 reset ( ) : void {
711727 if ( this . driver && this . options . connectExisting && 'kill' in this . driver ) {
712- ( this . driver as { kill ( ) : void } ) . kill ( ) ;
728+ ( this . driver as { kill ( ) : Promise < void > } ) . kill ( ) ;
713729 }
714730 this . driver = null ;
715731 this . currentContextId = null ;
@@ -831,7 +847,7 @@ export class FirefoxCore {
831847 async close ( ) : Promise < void > {
832848 if ( this . driver ) {
833849 if ( this . options . connectExisting && 'kill' in this . driver ) {
834- ( this . driver as { kill ( ) : void } ) . kill ( ) ;
850+ await ( this . driver as { kill ( ) : Promise < void > } ) . kill ( ) ;
835851 } else if ( 'quit' in this . driver ) {
836852 await ( this . driver as { quit ( ) : Promise < void > } ) . quit ( ) ;
837853 }
0 commit comments