@@ -14,28 +14,15 @@ import utils from './utils'
14
14
import type { Browser , BrowserInstance , GracefulShutdownOptions } from './types'
15
15
import os from 'os'
16
16
import mimeDb from 'mime-db'
17
- import { BrowserCriClient } from './browser-cri-client'
18
17
import type { BidiAutomation } from './bidi_automation'
19
18
import type { Automation } from '../automation'
20
19
import { getCtx } from '@packages/data-context'
21
- import { getError , SerializedError , CypressError } from '@packages/errors'
20
+ import { getError , CypressError } from '@packages/errors'
22
21
import type { BrowserLaunchOpts , BrowserNewTabOpts , RunModeVideoApi } from '@packages/types'
23
22
import type { RemoteConfig } from 'webdriver'
24
23
import type { GeckodriverParameters } from 'geckodriver'
25
24
import { WebDriver } from './webdriver'
26
25
27
- export class CDPFailedToStartFirefox extends Error {
28
- private static readonly ERROR_NAME = 'CDPFailedToStartFirefox'
29
- constructor ( message ) {
30
- super ( message )
31
- this . name = CDPFailedToStartFirefox . ERROR_NAME
32
- }
33
-
34
- public static isCDPFailedToStartFirefoxError ( error ?: SerializedError ) : error is CDPFailedToStartFirefox {
35
- return error ?. name === CDPFailedToStartFirefox . ERROR_NAME
36
- }
37
- }
38
-
39
26
const debug = Debug ( 'cypress:server:browsers:firefox' )
40
27
const debugVerbose = Debug ( 'cypress-verbose:server:browsers:firefox' )
41
28
@@ -335,20 +322,18 @@ const defaultPreferences = {
335
322
'browser.helperApps.neverAsk.saveToDisk' : downloadMimeTypes ,
336
323
}
337
324
338
- // CDP is deprecated in Firefox 129 and up.
325
+ // CDP was deprecated in Firefox 129 and up and was removed in Firefox 141 .
339
326
// To enable BiDi (without CDP), we need to set
340
327
// remote.active-protocol=1
341
- // In order to enable CDP (without BiDi), we need to set
328
+ // In order to enable CDP (without BiDi on firefox older than version 135 ), we need to set
342
329
// remote.active-protocol=2
343
330
// both can be enabled via
344
331
// remote.active-protocol=3
345
332
// @see https://fxdx.dev/deprecating-cdp-support-in-firefox-embracing-the-future-with-webdriver-bidi/
333
+ // @see https://fxdx.dev/webdriver-bidi-becomes-the-default-for-cypress-in-firefox/
346
334
// @see https://github.com/cypress-io/cypress/issues/29713
347
335
const ACTIVE_PROTOCOLS = Object . freeze ( {
348
336
BIDI : 1 ,
349
- CDP : 2 ,
350
- // this key isn't actively used but checked in here if we need to turn it on for internal debugging
351
- CDP_AND_BIDI : 3 ,
352
337
} )
353
338
354
339
const FIREFOX_HEADED_USERCSS = `\
@@ -387,7 +372,6 @@ toolbox {
387
372
}
388
373
`
389
374
390
- let browserCriClient : BrowserCriClient | undefined
391
375
let browserBidiClient : BidiAutomation | undefined
392
376
393
377
/**
@@ -396,34 +380,15 @@ let browserBidiClient: BidiAutomation | undefined
396
380
export function clearInstanceState ( options : GracefulShutdownOptions = { } ) {
397
381
debug ( 'clearing instance state' )
398
382
399
- if ( browserCriClient ) {
400
- debug ( 'closing remote interface client' )
401
- browserCriClient . close ( options . gracefulShutdown ) . catch ( ( ) => { } )
402
- browserCriClient = undefined
403
- }
404
-
405
383
if ( browserBidiClient ) {
406
384
debug ( 'unbinding bidi client events' )
407
385
browserBidiClient . close ( )
408
386
browserBidiClient = undefined
409
387
}
410
388
}
411
389
412
- function shouldUseBiDi ( browser : Browser ) : boolean {
413
- try {
414
- // Gating on firefox version 135 to turn on BiDi as this is when all of our internal Cypress tests were able to pass.
415
- return ( browser . family === 'firefox' && ! process . env . FORCE_FIREFOX_CDP && Number ( browser . majorVersion ) >= 135 )
416
- } catch ( err : unknown ) {
417
- return false
418
- }
419
- }
420
-
421
390
export async function connectToNewSpec ( browser : Browser , options : BrowserNewTabOpts , automation : Automation ) {
422
- if ( shouldUseBiDi ( browser ) ) {
423
- await firefoxUtil . connectToNewSpecBiDi ( options , automation , browserBidiClient ! )
424
- } else {
425
- await firefoxUtil . connectToNewSpecCDP ( options , automation , browserCriClient ! )
426
- }
391
+ await firefoxUtil . connectToNewSpecBiDi ( options , automation , browserBidiClient ! )
427
392
}
428
393
429
394
export function connectToExisting ( ) {
@@ -441,12 +406,6 @@ async function recordVideo (videoApi: RunModeVideoApi) {
441
406
}
442
407
443
408
export async function open ( browser : Browser , url : string , options : BrowserLaunchOpts , automation : Automation ) : Promise < BrowserInstance > {
444
- const USE_WEBDRIVER_BIDI = shouldUseBiDi ( browser )
445
-
446
- if ( ! USE_WEBDRIVER_BIDI ) {
447
- errors . warning ( 'CDP_FIREFOX_DEPRECATED' )
448
- }
449
-
450
409
const defaultLaunchOptions = utils . getDefaultLaunchOptions ( {
451
410
extensions : [ ] as string [ ] ,
452
411
preferences : _ . extend ( { } , defaultPreferences ) ,
@@ -459,7 +418,7 @@ export async function open (browser: Browser, url: string, options: BrowserLaunc
459
418
] ,
460
419
} )
461
420
462
- defaultLaunchOptions . preferences [ 'remote.active-protocols' ] = USE_WEBDRIVER_BIDI ? ACTIVE_PROTOCOLS . BIDI : ACTIVE_PROTOCOLS . CDP
421
+ defaultLaunchOptions . preferences [ 'remote.active-protocols' ] = ACTIVE_PROTOCOLS . BIDI
463
422
464
423
if ( browser . isHeadless ) {
465
424
defaultLaunchOptions . args . push ( '-headless' )
@@ -554,26 +513,6 @@ export async function open (browser: Browser, url: string, options: BrowserLaunc
554
513
555
514
debug ( 'firefox directories %o' , { path : profile . path ( ) , cacheDir, extensionDest } )
556
515
557
- const xulStorePath = path . join ( profile . path ( ) , 'xulstore.json' )
558
-
559
- // if user has set custom window.sizemode pref or it's the first time launching on this profile, write to xulStore.
560
- if ( ! await fs . pathExists ( xulStorePath ) ) {
561
- // this causes the browser to launch maximized, which chrome does by default
562
- // otherwise an arbitrary size will be picked for the window size
563
-
564
- // this used to not have an effect after first launch in 'interactive' mode.
565
- // However, since Cypress 13.15.1,
566
- // geckodriver creates unique profile names that copy over the xulstore.json to the used profile.
567
- // The copy is ultimately updated on the unique profile name and is destroyed when the browser is torn down,
568
- // so the values are not persisted. Cypress could hypothetically determine the profile is in use, copy the xulstore.json
569
- // out of the profile and try to persist it in the next created profile, but this method is likely error prone as it requires
570
- // moving/copying of files while creation/deletion of profiles occur, plus the ability to correlate the correct profile to the current run,
571
- // which there are not guarantees we can deterministically do this in open mode.
572
- const sizemode = 'maximized'
573
-
574
- await fs . writeJSON ( xulStorePath , { 'chrome://browser/content/browser.xhtml' : { 'main-window' : { 'width' : 1280 , 'height' : 1024 , sizemode } } } )
575
- }
576
-
577
516
launchOptions . preferences [ 'browser.cache.disk.parent_directory' ] = cacheDir
578
517
579
518
const userCSSPath = path . join ( profileDir , 'chrome' )
@@ -672,9 +611,6 @@ export async function open (browser: Browser, url: string, options: BrowserLaunc
672
611
args : launchOptions . args ,
673
612
prefs : launchOptions . preferences ,
674
613
} ,
675
- // @see https://firefox-source-docs.mozilla.org/testing/geckodriver/Capabilities.html#moz-debuggeraddress
676
- // we specify the debugger address option for Webdriver, which will return us the CDP address when the capability is returned.
677
- 'moz:debuggerAddress' : ! USE_WEBDRIVER_BIDI ,
678
614
// @see https://webdriver.io/docs/capabilities/#wdiogeckodriveroptions
679
615
// webdriver starts geckodriver with the correct options on behalf of Cypress
680
616
'wdio:geckodriverOptions' : geckoDriverOptions ,
@@ -744,26 +680,12 @@ export async function open (browser: Browser, url: string, options: BrowserLaunc
744
680
return browserReturnStatus || driverReturnStatus
745
681
}
746
682
747
- let cdpPort : number | undefined
748
-
749
- if ( ! USE_WEBDRIVER_BIDI ) {
750
- // In some cases, the webdriver session will NOT return the moz:debuggerAddress capability even though
751
- // we set it to true in the capabilities. This is out of our control, so when this happens, we fail the browser
752
- // and gracefully terminate the related processes and attempt to relaunch the browser in the hopes we get a
753
- // CDP address. @see https://github.com/cypress-io/cypress/issues/30352#issuecomment-2405701867 for more details.
754
- if ( ! webdriverClient . capabilities [ 'moz:debuggerAddress' ] ) {
755
- debugVerbose ( `firefox failed to spawn with CDP connection. Failing current instance and retrying` )
756
- // since this fails before the instance is created, we need to kill the processes here or else they will stay open
757
- browserInstanceWrapper . kill ( )
758
- throw new CDPFailedToStartFirefox ( `webdriver session failed to start CDP even though "moz:debuggerAddress" was provided. Please try to relaunch the browser` )
759
- }
760
-
761
- cdpPort = parseInt ( new URL ( `ws://${ webdriverClient . capabilities [ 'moz:debuggerAddress' ] } ` ) . port )
762
-
763
- debug ( `CDP running on port ${ cdpPort } ` )
764
-
765
- // makes it so get getRemoteDebuggingPort() is calculated correctly
766
- process . env . CYPRESS_REMOTE_DEBUGGING_PORT = cdpPort . toString ( )
683
+ // maximize the window if running headful and no width or height args are provided.
684
+ // NOTE: We used to do this with xulstore.json, but this is no longer possible with geckodriver
685
+ // as firefox will create the profile under the profile root that we cannot control and we cannot consistently provide
686
+ // a base 64 encoded profile.
687
+ if ( ! browser . isHeadless && ( ! launchOptions . args . includes ( '-width' ) || ! launchOptions . args . includes ( '-height' ) ) ) {
688
+ await webdriverClient . maximizeWindow ( )
767
689
}
768
690
769
691
// install the browser extensions
@@ -777,16 +699,7 @@ export async function open (browser: Browser, url: string, options: BrowserLaunc
777
699
} ) )
778
700
779
701
debug ( 'setting up firefox utils' )
780
- const client = await firefoxUtil . setup ( { automation, url, webdriverClient, remotePort : cdpPort , useWebDriverBiDi : USE_WEBDRIVER_BIDI , onError : options . onError } )
781
-
782
- if ( client instanceof BrowserCriClient ) {
783
- browserCriClient = client
784
- await utils . executeAfterBrowserLaunch ( browser , {
785
- webSocketDebuggerUrl : browserCriClient . getWebSocketDebuggerUrl ( ) ,
786
- } )
787
- } else {
788
- browserBidiClient = client
789
- }
702
+ browserBidiClient = await firefoxUtil . setup ( { automation, url, webdriverClient } )
790
703
} catch ( err : unknown ) {
791
704
errors . throwErr ( 'FIREFOX_COULD_NOT_CONNECT' , err as Error )
792
705
}
0 commit comments