mirror of
				https://github.com/microsoft/playwright.git
				synced 2025-06-26 21:40:17 +00:00 
			
		
		
		
	chore: align FFConnection with CRConnection (#27450)
				
					
				
			This commit is contained in:
		
							parent
							
								
									293c85935a
								
							
						
					
					
						commit
						cba2fc0752
					
				@ -25,13 +25,14 @@ import type { Page, PageBinding, PageDelegate } from '../page';
 | 
				
			|||||||
import type { ConnectionTransport } from '../transport';
 | 
					import type { ConnectionTransport } from '../transport';
 | 
				
			||||||
import type * as types from '../types';
 | 
					import type * as types from '../types';
 | 
				
			||||||
import type * as channels from '@protocol/channels';
 | 
					import type * as channels from '@protocol/channels';
 | 
				
			||||||
import { ConnectionEvents, FFConnection } from './ffConnection';
 | 
					import { ConnectionEvents, FFConnection, type FFSession } from './ffConnection';
 | 
				
			||||||
import { FFPage } from './ffPage';
 | 
					import { FFPage } from './ffPage';
 | 
				
			||||||
import type { Protocol } from './protocol';
 | 
					import type { Protocol } from './protocol';
 | 
				
			||||||
import type { SdkObject } from '../instrumentation';
 | 
					import type { SdkObject } from '../instrumentation';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class FFBrowser extends Browser {
 | 
					export class FFBrowser extends Browser {
 | 
				
			||||||
  _connection: FFConnection;
 | 
					  private _connection: FFConnection;
 | 
				
			||||||
 | 
					  readonly session: FFSession;
 | 
				
			||||||
  readonly _ffPages: Map<string, FFPage>;
 | 
					  readonly _ffPages: Map<string, FFPage>;
 | 
				
			||||||
  readonly _contexts: Map<string, FFBrowserContext>;
 | 
					  readonly _contexts: Map<string, FFBrowserContext>;
 | 
				
			||||||
  private _version = '';
 | 
					  private _version = '';
 | 
				
			||||||
@ -46,7 +47,7 @@ export class FFBrowser extends Browser {
 | 
				
			|||||||
    if (Object.keys(kBandaidFirefoxUserPrefs).length)
 | 
					    if (Object.keys(kBandaidFirefoxUserPrefs).length)
 | 
				
			||||||
      firefoxUserPrefs = { ...kBandaidFirefoxUserPrefs, ...firefoxUserPrefs };
 | 
					      firefoxUserPrefs = { ...kBandaidFirefoxUserPrefs, ...firefoxUserPrefs };
 | 
				
			||||||
    const promises: Promise<any>[] = [
 | 
					    const promises: Promise<any>[] = [
 | 
				
			||||||
      connection.send('Browser.enable', {
 | 
					      browser.session.send('Browser.enable', {
 | 
				
			||||||
        attachToDefaultContext: !!options.persistent,
 | 
					        attachToDefaultContext: !!options.persistent,
 | 
				
			||||||
        userPrefs: Object.entries(firefoxUserPrefs).map(([name, value]) => ({ name, value })),
 | 
					        userPrefs: Object.entries(firefoxUserPrefs).map(([name, value]) => ({ name, value })),
 | 
				
			||||||
      }),
 | 
					      }),
 | 
				
			||||||
@ -57,7 +58,7 @@ export class FFBrowser extends Browser {
 | 
				
			|||||||
      promises.push((browser._defaultContext as FFBrowserContext)._initialize());
 | 
					      promises.push((browser._defaultContext as FFBrowserContext)._initialize());
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    if (options.proxy)
 | 
					    if (options.proxy)
 | 
				
			||||||
      promises.push(browser._connection.send('Browser.setBrowserProxy', toJugglerProxyOptions(options.proxy)));
 | 
					      promises.push(browser.session.send('Browser.setBrowserProxy', toJugglerProxyOptions(options.proxy)));
 | 
				
			||||||
    await Promise.all(promises);
 | 
					    await Promise.all(promises);
 | 
				
			||||||
    return browser;
 | 
					    return browser;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@ -65,18 +66,19 @@ export class FFBrowser extends Browser {
 | 
				
			|||||||
  constructor(parent: SdkObject, connection: FFConnection, options: BrowserOptions) {
 | 
					  constructor(parent: SdkObject, connection: FFConnection, options: BrowserOptions) {
 | 
				
			||||||
    super(parent, options);
 | 
					    super(parent, options);
 | 
				
			||||||
    this._connection = connection;
 | 
					    this._connection = connection;
 | 
				
			||||||
 | 
					    this.session = connection.rootSession;
 | 
				
			||||||
    this._ffPages = new Map();
 | 
					    this._ffPages = new Map();
 | 
				
			||||||
    this._contexts = new Map();
 | 
					    this._contexts = new Map();
 | 
				
			||||||
    this._connection.on(ConnectionEvents.Disconnected, () => this._onDisconnect());
 | 
					    this._connection.on(ConnectionEvents.Disconnected, () => this._onDisconnect());
 | 
				
			||||||
    this._connection.on('Browser.attachedToTarget', this._onAttachedToTarget.bind(this));
 | 
					    this.session.on('Browser.attachedToTarget', this._onAttachedToTarget.bind(this));
 | 
				
			||||||
    this._connection.on('Browser.detachedFromTarget', this._onDetachedFromTarget.bind(this));
 | 
					    this.session.on('Browser.detachedFromTarget', this._onDetachedFromTarget.bind(this));
 | 
				
			||||||
    this._connection.on('Browser.downloadCreated', this._onDownloadCreated.bind(this));
 | 
					    this.session.on('Browser.downloadCreated', this._onDownloadCreated.bind(this));
 | 
				
			||||||
    this._connection.on('Browser.downloadFinished', this._onDownloadFinished.bind(this));
 | 
					    this.session.on('Browser.downloadFinished', this._onDownloadFinished.bind(this));
 | 
				
			||||||
    this._connection.on('Browser.videoRecordingFinished', this._onVideoRecordingFinished.bind(this));
 | 
					    this.session.on('Browser.videoRecordingFinished', this._onVideoRecordingFinished.bind(this));
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  async _initVersion() {
 | 
					  async _initVersion() {
 | 
				
			||||||
    const result = await this._connection.send('Browser.getInfo');
 | 
					    const result = await this.session.send('Browser.getInfo');
 | 
				
			||||||
    this._version = result.version.substring(result.version.indexOf('/') + 1);
 | 
					    this._version = result.version.substring(result.version.indexOf('/') + 1);
 | 
				
			||||||
    this._userAgent = result.userAgent;
 | 
					    this._userAgent = result.userAgent;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@ -88,7 +90,7 @@ export class FFBrowser extends Browser {
 | 
				
			|||||||
  async doCreateNewContext(options: channels.BrowserNewContextParams): Promise<BrowserContext> {
 | 
					  async doCreateNewContext(options: channels.BrowserNewContextParams): Promise<BrowserContext> {
 | 
				
			||||||
    if (options.isMobile)
 | 
					    if (options.isMobile)
 | 
				
			||||||
      throw new Error('options.isMobile is not supported in Firefox');
 | 
					      throw new Error('options.isMobile is not supported in Firefox');
 | 
				
			||||||
    const { browserContextId } = await this._connection.send('Browser.createBrowserContext', { removeOnDetach: true });
 | 
					    const { browserContextId } = await this.session.send('Browser.createBrowserContext', { removeOnDetach: true });
 | 
				
			||||||
    const context = new FFBrowserContext(this, browserContextId, options);
 | 
					    const context = new FFBrowserContext(this, browserContextId, options);
 | 
				
			||||||
    await context._initialize();
 | 
					    await context._initialize();
 | 
				
			||||||
    this._contexts.set(browserContextId, context);
 | 
					    this._contexts.set(browserContextId, context);
 | 
				
			||||||
@ -178,7 +180,7 @@ export class FFBrowserContext extends BrowserContext {
 | 
				
			|||||||
    const browserContextId = this._browserContextId;
 | 
					    const browserContextId = this._browserContextId;
 | 
				
			||||||
    const promises: Promise<any>[] = [super._initialize()];
 | 
					    const promises: Promise<any>[] = [super._initialize()];
 | 
				
			||||||
    if (this._options.acceptDownloads !== 'internal-browser-default') {
 | 
					    if (this._options.acceptDownloads !== 'internal-browser-default') {
 | 
				
			||||||
      promises.push(this._browser._connection.send('Browser.setDownloadOptions', {
 | 
					      promises.push(this._browser.session.send('Browser.setDownloadOptions', {
 | 
				
			||||||
        browserContextId,
 | 
					        browserContextId,
 | 
				
			||||||
        downloadOptions: {
 | 
					        downloadOptions: {
 | 
				
			||||||
          behavior: this._options.acceptDownloads === 'accept' ? 'saveToDisk' : 'cancel',
 | 
					          behavior: this._options.acceptDownloads === 'accept' ? 'saveToDisk' : 'cancel',
 | 
				
			||||||
@ -191,22 +193,22 @@ export class FFBrowserContext extends BrowserContext {
 | 
				
			|||||||
        viewportSize: { width: this._options.viewport.width, height: this._options.viewport.height },
 | 
					        viewportSize: { width: this._options.viewport.width, height: this._options.viewport.height },
 | 
				
			||||||
        deviceScaleFactor: this._options.deviceScaleFactor || 1,
 | 
					        deviceScaleFactor: this._options.deviceScaleFactor || 1,
 | 
				
			||||||
      };
 | 
					      };
 | 
				
			||||||
      promises.push(this._browser._connection.send('Browser.setDefaultViewport', { browserContextId, viewport }));
 | 
					      promises.push(this._browser.session.send('Browser.setDefaultViewport', { browserContextId, viewport }));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    if (this._options.hasTouch)
 | 
					    if (this._options.hasTouch)
 | 
				
			||||||
      promises.push(this._browser._connection.send('Browser.setTouchOverride', { browserContextId, hasTouch: true }));
 | 
					      promises.push(this._browser.session.send('Browser.setTouchOverride', { browserContextId, hasTouch: true }));
 | 
				
			||||||
    if (this._options.userAgent)
 | 
					    if (this._options.userAgent)
 | 
				
			||||||
      promises.push(this._browser._connection.send('Browser.setUserAgentOverride', { browserContextId, userAgent: this._options.userAgent }));
 | 
					      promises.push(this._browser.session.send('Browser.setUserAgentOverride', { browserContextId, userAgent: this._options.userAgent }));
 | 
				
			||||||
    if (this._options.bypassCSP)
 | 
					    if (this._options.bypassCSP)
 | 
				
			||||||
      promises.push(this._browser._connection.send('Browser.setBypassCSP', { browserContextId, bypassCSP: true }));
 | 
					      promises.push(this._browser.session.send('Browser.setBypassCSP', { browserContextId, bypassCSP: true }));
 | 
				
			||||||
    if (this._options.ignoreHTTPSErrors)
 | 
					    if (this._options.ignoreHTTPSErrors)
 | 
				
			||||||
      promises.push(this._browser._connection.send('Browser.setIgnoreHTTPSErrors', { browserContextId, ignoreHTTPSErrors: true }));
 | 
					      promises.push(this._browser.session.send('Browser.setIgnoreHTTPSErrors', { browserContextId, ignoreHTTPSErrors: true }));
 | 
				
			||||||
    if (this._options.javaScriptEnabled === false)
 | 
					    if (this._options.javaScriptEnabled === false)
 | 
				
			||||||
      promises.push(this._browser._connection.send('Browser.setJavaScriptDisabled', { browserContextId, javaScriptDisabled: true }));
 | 
					      promises.push(this._browser.session.send('Browser.setJavaScriptDisabled', { browserContextId, javaScriptDisabled: true }));
 | 
				
			||||||
    if (this._options.locale)
 | 
					    if (this._options.locale)
 | 
				
			||||||
      promises.push(this._browser._connection.send('Browser.setLocaleOverride', { browserContextId, locale: this._options.locale }));
 | 
					      promises.push(this._browser.session.send('Browser.setLocaleOverride', { browserContextId, locale: this._options.locale }));
 | 
				
			||||||
    if (this._options.timezoneId)
 | 
					    if (this._options.timezoneId)
 | 
				
			||||||
      promises.push(this._browser._connection.send('Browser.setTimezoneOverride', { browserContextId, timezoneId: this._options.timezoneId }));
 | 
					      promises.push(this._browser.session.send('Browser.setTimezoneOverride', { browserContextId, timezoneId: this._options.timezoneId }));
 | 
				
			||||||
    if (this._options.extraHTTPHeaders || this._options.locale)
 | 
					    if (this._options.extraHTTPHeaders || this._options.locale)
 | 
				
			||||||
      promises.push(this.setExtraHTTPHeaders(this._options.extraHTTPHeaders || []));
 | 
					      promises.push(this.setExtraHTTPHeaders(this._options.extraHTTPHeaders || []));
 | 
				
			||||||
    if (this._options.httpCredentials)
 | 
					    if (this._options.httpCredentials)
 | 
				
			||||||
@ -216,26 +218,26 @@ export class FFBrowserContext extends BrowserContext {
 | 
				
			|||||||
    if (this._options.offline)
 | 
					    if (this._options.offline)
 | 
				
			||||||
      promises.push(this.setOffline(this._options.offline));
 | 
					      promises.push(this.setOffline(this._options.offline));
 | 
				
			||||||
    if (this._options.colorScheme !== 'no-override') {
 | 
					    if (this._options.colorScheme !== 'no-override') {
 | 
				
			||||||
      promises.push(this._browser._connection.send('Browser.setColorScheme', {
 | 
					      promises.push(this._browser.session.send('Browser.setColorScheme', {
 | 
				
			||||||
        browserContextId,
 | 
					        browserContextId,
 | 
				
			||||||
        colorScheme: this._options.colorScheme !== undefined  ? this._options.colorScheme : 'light',
 | 
					        colorScheme: this._options.colorScheme !== undefined  ? this._options.colorScheme : 'light',
 | 
				
			||||||
      }));
 | 
					      }));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    if (this._options.reducedMotion !== 'no-override') {
 | 
					    if (this._options.reducedMotion !== 'no-override') {
 | 
				
			||||||
      promises.push(this._browser._connection.send('Browser.setReducedMotion', {
 | 
					      promises.push(this._browser.session.send('Browser.setReducedMotion', {
 | 
				
			||||||
        browserContextId,
 | 
					        browserContextId,
 | 
				
			||||||
        reducedMotion: this._options.reducedMotion !== undefined  ? this._options.reducedMotion : 'no-preference',
 | 
					        reducedMotion: this._options.reducedMotion !== undefined  ? this._options.reducedMotion : 'no-preference',
 | 
				
			||||||
      }));
 | 
					      }));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    if (this._options.forcedColors !== 'no-override') {
 | 
					    if (this._options.forcedColors !== 'no-override') {
 | 
				
			||||||
      promises.push(this._browser._connection.send('Browser.setForcedColors', {
 | 
					      promises.push(this._browser.session.send('Browser.setForcedColors', {
 | 
				
			||||||
        browserContextId,
 | 
					        browserContextId,
 | 
				
			||||||
        forcedColors: this._options.forcedColors !== undefined  ? this._options.forcedColors : 'none',
 | 
					        forcedColors: this._options.forcedColors !== undefined  ? this._options.forcedColors : 'none',
 | 
				
			||||||
      }));
 | 
					      }));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    if (this._options.recordVideo) {
 | 
					    if (this._options.recordVideo) {
 | 
				
			||||||
      promises.push(this._ensureVideosPath().then(() => {
 | 
					      promises.push(this._ensureVideosPath().then(() => {
 | 
				
			||||||
        return this._browser._connection.send('Browser.setVideoRecordingOptions', {
 | 
					        return this._browser.session.send('Browser.setVideoRecordingOptions', {
 | 
				
			||||||
          // validateBrowserContextOptions ensures correct video size.
 | 
					          // validateBrowserContextOptions ensures correct video size.
 | 
				
			||||||
          options: {
 | 
					          options: {
 | 
				
			||||||
            ...this._options.recordVideo!.size!,
 | 
					            ...this._options.recordVideo!.size!,
 | 
				
			||||||
@ -246,7 +248,7 @@ export class FFBrowserContext extends BrowserContext {
 | 
				
			|||||||
      }));
 | 
					      }));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    if (this._options.proxy) {
 | 
					    if (this._options.proxy) {
 | 
				
			||||||
      promises.push(this._browser._connection.send('Browser.setContextProxy', {
 | 
					      promises.push(this._browser.session.send('Browser.setContextProxy', {
 | 
				
			||||||
        browserContextId: this._browserContextId,
 | 
					        browserContextId: this._browserContextId,
 | 
				
			||||||
        ...toJugglerProxyOptions(this._options.proxy)
 | 
					        ...toJugglerProxyOptions(this._options.proxy)
 | 
				
			||||||
      }));
 | 
					      }));
 | 
				
			||||||
@ -265,7 +267,7 @@ export class FFBrowserContext extends BrowserContext {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  async newPageDelegate(): Promise<PageDelegate> {
 | 
					  async newPageDelegate(): Promise<PageDelegate> {
 | 
				
			||||||
    assertBrowserContextIsNotOwned(this);
 | 
					    assertBrowserContextIsNotOwned(this);
 | 
				
			||||||
    const { targetId } = await this._browser._connection.send('Browser.newPage', {
 | 
					    const { targetId } = await this._browser.session.send('Browser.newPage', {
 | 
				
			||||||
      browserContextId: this._browserContextId
 | 
					      browserContextId: this._browserContextId
 | 
				
			||||||
    }).catch(e =>  {
 | 
					    }).catch(e =>  {
 | 
				
			||||||
      if (e.message.includes('Failed to override timezone'))
 | 
					      if (e.message.includes('Failed to override timezone'))
 | 
				
			||||||
@ -276,7 +278,7 @@ export class FFBrowserContext extends BrowserContext {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  async doGetCookies(urls: string[]): Promise<channels.NetworkCookie[]> {
 | 
					  async doGetCookies(urls: string[]): Promise<channels.NetworkCookie[]> {
 | 
				
			||||||
    const { cookies } = await this._browser._connection.send('Browser.getCookies', { browserContextId: this._browserContextId });
 | 
					    const { cookies } = await this._browser.session.send('Browser.getCookies', { browserContextId: this._browserContextId });
 | 
				
			||||||
    return network.filterCookies(cookies.map(c => {
 | 
					    return network.filterCookies(cookies.map(c => {
 | 
				
			||||||
      const copy: any = { ... c };
 | 
					      const copy: any = { ... c };
 | 
				
			||||||
      delete copy.size;
 | 
					      delete copy.size;
 | 
				
			||||||
@ -290,11 +292,11 @@ export class FFBrowserContext extends BrowserContext {
 | 
				
			|||||||
      ...c,
 | 
					      ...c,
 | 
				
			||||||
      expires: c.expires === -1 ? undefined : c.expires,
 | 
					      expires: c.expires === -1 ? undefined : c.expires,
 | 
				
			||||||
    }));
 | 
					    }));
 | 
				
			||||||
    await this._browser._connection.send('Browser.setCookies', { browserContextId: this._browserContextId, cookies: cc });
 | 
					    await this._browser.session.send('Browser.setCookies', { browserContextId: this._browserContextId, cookies: cc });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  async clearCookies() {
 | 
					  async clearCookies() {
 | 
				
			||||||
    await this._browser._connection.send('Browser.clearCookies', { browserContextId: this._browserContextId });
 | 
					    await this._browser.session.send('Browser.clearCookies', { browserContextId: this._browserContextId });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  async doGrantPermissions(origin: string, permissions: string[]) {
 | 
					  async doGrantPermissions(origin: string, permissions: string[]) {
 | 
				
			||||||
@ -310,17 +312,17 @@ export class FFBrowserContext extends BrowserContext {
 | 
				
			|||||||
        throw new Error('Unknown permission: ' + permission);
 | 
					        throw new Error('Unknown permission: ' + permission);
 | 
				
			||||||
      return protocolPermission;
 | 
					      return protocolPermission;
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
    await this._browser._connection.send('Browser.grantPermissions', { origin: origin, browserContextId: this._browserContextId, permissions: filtered });
 | 
					    await this._browser.session.send('Browser.grantPermissions', { origin: origin, browserContextId: this._browserContextId, permissions: filtered });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  async doClearPermissions() {
 | 
					  async doClearPermissions() {
 | 
				
			||||||
    await this._browser._connection.send('Browser.resetPermissions', { browserContextId: this._browserContextId });
 | 
					    await this._browser.session.send('Browser.resetPermissions', { browserContextId: this._browserContextId });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  async setGeolocation(geolocation?: types.Geolocation): Promise<void> {
 | 
					  async setGeolocation(geolocation?: types.Geolocation): Promise<void> {
 | 
				
			||||||
    verifyGeolocation(geolocation);
 | 
					    verifyGeolocation(geolocation);
 | 
				
			||||||
    this._options.geolocation = geolocation;
 | 
					    this._options.geolocation = geolocation;
 | 
				
			||||||
    await this._browser._connection.send('Browser.setGeolocationOverride', { browserContextId: this._browserContextId, geolocation: geolocation || null });
 | 
					    await this._browser.session.send('Browser.setGeolocationOverride', { browserContextId: this._browserContextId, geolocation: geolocation || null });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  async setExtraHTTPHeaders(headers: types.HeadersArray): Promise<void> {
 | 
					  async setExtraHTTPHeaders(headers: types.HeadersArray): Promise<void> {
 | 
				
			||||||
@ -328,33 +330,33 @@ export class FFBrowserContext extends BrowserContext {
 | 
				
			|||||||
    let allHeaders = this._options.extraHTTPHeaders;
 | 
					    let allHeaders = this._options.extraHTTPHeaders;
 | 
				
			||||||
    if (this._options.locale)
 | 
					    if (this._options.locale)
 | 
				
			||||||
      allHeaders = network.mergeHeaders([allHeaders, network.singleHeader('Accept-Language', this._options.locale)]);
 | 
					      allHeaders = network.mergeHeaders([allHeaders, network.singleHeader('Accept-Language', this._options.locale)]);
 | 
				
			||||||
    await this._browser._connection.send('Browser.setExtraHTTPHeaders', { browserContextId: this._browserContextId, headers: allHeaders });
 | 
					    await this._browser.session.send('Browser.setExtraHTTPHeaders', { browserContextId: this._browserContextId, headers: allHeaders });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  async setUserAgent(userAgent: string | undefined): Promise<void> {
 | 
					  async setUserAgent(userAgent: string | undefined): Promise<void> {
 | 
				
			||||||
    await this._browser._connection.send('Browser.setUserAgentOverride', { browserContextId: this._browserContextId, userAgent: userAgent || null });
 | 
					    await this._browser.session.send('Browser.setUserAgentOverride', { browserContextId: this._browserContextId, userAgent: userAgent || null });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  async setOffline(offline: boolean): Promise<void> {
 | 
					  async setOffline(offline: boolean): Promise<void> {
 | 
				
			||||||
    this._options.offline = offline;
 | 
					    this._options.offline = offline;
 | 
				
			||||||
    await this._browser._connection.send('Browser.setOnlineOverride', { browserContextId: this._browserContextId, override: offline ? 'offline' : 'online' });
 | 
					    await this._browser.session.send('Browser.setOnlineOverride', { browserContextId: this._browserContextId, override: offline ? 'offline' : 'online' });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  async doSetHTTPCredentials(httpCredentials?: types.Credentials): Promise<void> {
 | 
					  async doSetHTTPCredentials(httpCredentials?: types.Credentials): Promise<void> {
 | 
				
			||||||
    this._options.httpCredentials = httpCredentials;
 | 
					    this._options.httpCredentials = httpCredentials;
 | 
				
			||||||
    await this._browser._connection.send('Browser.setHTTPCredentials', { browserContextId: this._browserContextId, credentials: httpCredentials || null });
 | 
					    await this._browser.session.send('Browser.setHTTPCredentials', { browserContextId: this._browserContextId, credentials: httpCredentials || null });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  async doAddInitScript(source: string) {
 | 
					  async doAddInitScript(source: string) {
 | 
				
			||||||
    await this._browser._connection.send('Browser.setInitScripts', { browserContextId: this._browserContextId, scripts: this.initScripts.map(script => ({ script })) });
 | 
					    await this._browser.session.send('Browser.setInitScripts', { browserContextId: this._browserContextId, scripts: this.initScripts.map(script => ({ script })) });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  async doRemoveInitScripts() {
 | 
					  async doRemoveInitScripts() {
 | 
				
			||||||
    await this._browser._connection.send('Browser.setInitScripts', { browserContextId: this._browserContextId, scripts: [] });
 | 
					    await this._browser.session.send('Browser.setInitScripts', { browserContextId: this._browserContextId, scripts: [] });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  async doExposeBinding(binding: PageBinding) {
 | 
					  async doExposeBinding(binding: PageBinding) {
 | 
				
			||||||
    await this._browser._connection.send('Browser.addBinding', { browserContextId: this._browserContextId, name: binding.name, script: binding.source });
 | 
					    await this._browser.session.send('Browser.addBinding', { browserContextId: this._browserContextId, name: binding.name, script: binding.source });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  async doRemoveExposedBindings() {
 | 
					  async doRemoveExposedBindings() {
 | 
				
			||||||
@ -364,20 +366,20 @@ export class FFBrowserContext extends BrowserContext {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  async doUpdateRequestInterception(): Promise<void> {
 | 
					  async doUpdateRequestInterception(): Promise<void> {
 | 
				
			||||||
    await this._browser._connection.send('Browser.setRequestInterception', { browserContextId: this._browserContextId, enabled: !!this._requestInterceptor });
 | 
					    await this._browser.session.send('Browser.setRequestInterception', { browserContextId: this._browserContextId, enabled: !!this._requestInterceptor });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  onClosePersistent() {}
 | 
					  onClosePersistent() {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  override async clearCache(): Promise<void> {
 | 
					  override async clearCache(): Promise<void> {
 | 
				
			||||||
    // Clearing only the context cache does not work: https://bugzilla.mozilla.org/show_bug.cgi?id=1819147
 | 
					    // Clearing only the context cache does not work: https://bugzilla.mozilla.org/show_bug.cgi?id=1819147
 | 
				
			||||||
    await this._browser._connection.send('Browser.clearCache');
 | 
					    await this._browser.session.send('Browser.clearCache');
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  async doClose() {
 | 
					  async doClose() {
 | 
				
			||||||
    if (!this._browserContextId) {
 | 
					    if (!this._browserContextId) {
 | 
				
			||||||
      if (this._options.recordVideo) {
 | 
					      if (this._options.recordVideo) {
 | 
				
			||||||
        await this._browser._connection.send('Browser.setVideoRecordingOptions', {
 | 
					        await this._browser.session.send('Browser.setVideoRecordingOptions', {
 | 
				
			||||||
          options: undefined,
 | 
					          options: undefined,
 | 
				
			||||||
          browserContextId: this._browserContextId
 | 
					          browserContextId: this._browserContextId
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
@ -385,13 +387,13 @@ export class FFBrowserContext extends BrowserContext {
 | 
				
			|||||||
      // Closing persistent context should close the browser.
 | 
					      // Closing persistent context should close the browser.
 | 
				
			||||||
      await this._browser.close();
 | 
					      await this._browser.close();
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      await this._browser._connection.send('Browser.removeBrowserContext', { browserContextId: this._browserContextId });
 | 
					      await this._browser.session.send('Browser.removeBrowserContext', { browserContextId: this._browserContextId });
 | 
				
			||||||
      this._browser._contexts.delete(this._browserContextId);
 | 
					      this._browser._contexts.delete(this._browserContextId);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  async cancelDownload(uuid: string) {
 | 
					  async cancelDownload(uuid: string) {
 | 
				
			||||||
    await this._browser._connection.send('Browser.cancelDownload', { uuid });
 | 
					    await this._browser.session.send('Browser.cancelDownload', { uuid });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -16,7 +16,6 @@
 | 
				
			|||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { EventEmitter } from 'events';
 | 
					import { EventEmitter } from 'events';
 | 
				
			||||||
import { assert } from '../../utils';
 | 
					 | 
				
			||||||
import type { ConnectionTransport, ProtocolRequest, ProtocolResponse } from '../transport';
 | 
					import type { ConnectionTransport, ProtocolRequest, ProtocolResponse } from '../transport';
 | 
				
			||||||
import type { Protocol } from './protocol';
 | 
					import type { Protocol } from './protocol';
 | 
				
			||||||
import { rewriteErrorMessage } from '../../utils/stackTrace';
 | 
					import { rewriteErrorMessage } from '../../utils/stackTrace';
 | 
				
			||||||
@ -36,19 +35,14 @@ export const kBrowserCloseMessageId = -9999;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
export class FFConnection extends EventEmitter {
 | 
					export class FFConnection extends EventEmitter {
 | 
				
			||||||
  private _lastId: number;
 | 
					  private _lastId: number;
 | 
				
			||||||
  private _callbacks: Map<number, {resolve: (o: any) => void, reject: (e: ProtocolError) => void, error: ProtocolError, method: string}>;
 | 
					 | 
				
			||||||
  private _transport: ConnectionTransport;
 | 
					  private _transport: ConnectionTransport;
 | 
				
			||||||
  private readonly _protocolLogger: ProtocolLogger;
 | 
					  private readonly _protocolLogger: ProtocolLogger;
 | 
				
			||||||
  private readonly _browserLogsCollector: RecentLogsCollector;
 | 
					  private readonly _browserLogsCollector: RecentLogsCollector;
 | 
				
			||||||
 | 
					  _browserDisconnectedLogs: string | undefined;
 | 
				
			||||||
 | 
					  readonly rootSession: FFSession;
 | 
				
			||||||
  readonly _sessions: Map<string, FFSession>;
 | 
					  readonly _sessions: Map<string, FFSession>;
 | 
				
			||||||
  _closed: boolean;
 | 
					  _closed: boolean;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  override on: <T extends keyof Protocol.Events | symbol>(event: T, listener: (payload: T extends symbol ? any : Protocol.Events[T extends keyof Protocol.Events ? T : never]) => void) => this;
 | 
					 | 
				
			||||||
  override addListener: <T extends keyof Protocol.Events | symbol>(event: T, listener: (payload: T extends symbol ? any : Protocol.Events[T extends keyof Protocol.Events ? T : never]) => void) => this;
 | 
					 | 
				
			||||||
  override off: <T extends keyof Protocol.Events | symbol>(event: T, listener: (payload: T extends symbol ? any : Protocol.Events[T extends keyof Protocol.Events ? T : never]) => void) => this;
 | 
					 | 
				
			||||||
  override removeListener: <T extends keyof Protocol.Events | symbol>(event: T, listener: (payload: T extends symbol ? any : Protocol.Events[T extends keyof Protocol.Events ? T : never]) => void) => this;
 | 
					 | 
				
			||||||
  override once: <T extends keyof Protocol.Events | symbol>(event: T, listener: (payload: T extends symbol ? any : Protocol.Events[T extends keyof Protocol.Events ? T : never]) => void) => this;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  constructor(transport: ConnectionTransport, protocolLogger: ProtocolLogger, browserLogsCollector: RecentLogsCollector) {
 | 
					  constructor(transport: ConnectionTransport, protocolLogger: ProtocolLogger, browserLogsCollector: RecentLogsCollector) {
 | 
				
			||||||
    super();
 | 
					    super();
 | 
				
			||||||
    this.setMaxListeners(0);
 | 
					    this.setMaxListeners(0);
 | 
				
			||||||
@ -56,43 +50,20 @@ export class FFConnection extends EventEmitter {
 | 
				
			|||||||
    this._protocolLogger = protocolLogger;
 | 
					    this._protocolLogger = protocolLogger;
 | 
				
			||||||
    this._browserLogsCollector = browserLogsCollector;
 | 
					    this._browserLogsCollector = browserLogsCollector;
 | 
				
			||||||
    this._lastId = 0;
 | 
					    this._lastId = 0;
 | 
				
			||||||
    this._callbacks = new Map();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    this._sessions = new Map();
 | 
					    this._sessions = new Map();
 | 
				
			||||||
    this._closed = false;
 | 
					    this._closed = false;
 | 
				
			||||||
 | 
					    this.rootSession = new FFSession(this, '', message => this._rawSend(message));
 | 
				
			||||||
    this.on = super.on;
 | 
					    this._sessions.set('', this.rootSession);
 | 
				
			||||||
    this.addListener = super.addListener;
 | 
					 | 
				
			||||||
    this.off = super.removeListener;
 | 
					 | 
				
			||||||
    this.removeListener = super.removeListener;
 | 
					 | 
				
			||||||
    this.once = super.once;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    this._transport.onmessage = this._onMessage.bind(this);
 | 
					    this._transport.onmessage = this._onMessage.bind(this);
 | 
				
			||||||
    // onclose should be set last, since it can be immediately called.
 | 
					    // onclose should be set last, since it can be immediately called.
 | 
				
			||||||
    this._transport.onclose = this._onClose.bind(this);
 | 
					    this._transport.onclose = this._onClose.bind(this);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  async send<T extends keyof Protocol.CommandParameters>(
 | 
					 | 
				
			||||||
    method: T,
 | 
					 | 
				
			||||||
    params?: Protocol.CommandParameters[T]
 | 
					 | 
				
			||||||
  ): Promise<Protocol.CommandReturnValues[T]> {
 | 
					 | 
				
			||||||
    this._checkClosed(method);
 | 
					 | 
				
			||||||
    const id = this.nextMessageId();
 | 
					 | 
				
			||||||
    this._rawSend({ id, method, params });
 | 
					 | 
				
			||||||
    return new Promise((resolve, reject) => {
 | 
					 | 
				
			||||||
      this._callbacks.set(id, { resolve, reject, error: new ProtocolError(false), method });
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  nextMessageId(): number {
 | 
					  nextMessageId(): number {
 | 
				
			||||||
    return ++this._lastId;
 | 
					    return ++this._lastId;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  _checkClosed(method: string) {
 | 
					 | 
				
			||||||
    if (this._closed)
 | 
					 | 
				
			||||||
      throw new ProtocolError(true, `${method}): Browser closed.` + helper.formatBrowserLogs(this._browserLogsCollector.recentLogs()));
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  _rawSend(message: ProtocolRequest) {
 | 
					  _rawSend(message: ProtocolRequest) {
 | 
				
			||||||
    this._protocolLogger('send', message);
 | 
					    this._protocolLogger('send', message);
 | 
				
			||||||
    this._transport.send(message);
 | 
					    this._transport.send(message);
 | 
				
			||||||
@ -102,36 +73,17 @@ export class FFConnection extends EventEmitter {
 | 
				
			|||||||
    this._protocolLogger('receive', message);
 | 
					    this._protocolLogger('receive', message);
 | 
				
			||||||
    if (message.id === kBrowserCloseMessageId)
 | 
					    if (message.id === kBrowserCloseMessageId)
 | 
				
			||||||
      return;
 | 
					      return;
 | 
				
			||||||
    if (message.sessionId) {
 | 
					    const session = this._sessions.get(message.sessionId || '');
 | 
				
			||||||
      const session = this._sessions.get(message.sessionId);
 | 
					    if (session)
 | 
				
			||||||
      if (session)
 | 
					      session.dispatchMessage(message);
 | 
				
			||||||
        session.dispatchMessage(message);
 | 
					 | 
				
			||||||
    } else if (message.id) {
 | 
					 | 
				
			||||||
      const callback = this._callbacks.get(message.id);
 | 
					 | 
				
			||||||
      // Callbacks could be all rejected if someone has called `.dispose()`.
 | 
					 | 
				
			||||||
      if (callback) {
 | 
					 | 
				
			||||||
        this._callbacks.delete(message.id);
 | 
					 | 
				
			||||||
        if (message.error)
 | 
					 | 
				
			||||||
          callback.reject(createProtocolError(callback.error, callback.method, message.error));
 | 
					 | 
				
			||||||
        else
 | 
					 | 
				
			||||||
          callback.resolve(message.result);
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
      Promise.resolve().then(() => this.emit(message.method!, message.params));
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  _onClose() {
 | 
					  _onClose() {
 | 
				
			||||||
    this._closed = true;
 | 
					    this._closed = true;
 | 
				
			||||||
    this._transport.onmessage = undefined;
 | 
					    this._transport.onmessage = undefined;
 | 
				
			||||||
    this._transport.onclose = undefined;
 | 
					    this._transport.onclose = undefined;
 | 
				
			||||||
    const formattedBrowserLogs = helper.formatBrowserLogs(this._browserLogsCollector.recentLogs());
 | 
					    this._browserDisconnectedLogs = helper.formatBrowserLogs(this._browserLogsCollector.recentLogs());
 | 
				
			||||||
    for (const callback of this._callbacks.values()) {
 | 
					    this.rootSession.dispose();
 | 
				
			||||||
      const error = rewriteErrorMessage(callback.error, `Protocol error (${callback.method}): Browser closed.` + formattedBrowserLogs);
 | 
					 | 
				
			||||||
      error.sessionClosed =  true;
 | 
					 | 
				
			||||||
      callback.reject(error);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    this._callbacks.clear();
 | 
					 | 
				
			||||||
    Promise.resolve().then(() => this.emit(ConnectionEvents.Disconnected));
 | 
					    Promise.resolve().then(() => this.emit(ConnectionEvents.Disconnected));
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -179,15 +131,24 @@ export class FFSession extends EventEmitter {
 | 
				
			|||||||
    this._crashed = true;
 | 
					    this._crashed = true;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private _closedErrorMessage() {
 | 
				
			||||||
 | 
					    if (this._crashed)
 | 
				
			||||||
 | 
					      return 'Target crashed';
 | 
				
			||||||
 | 
					    if (this._connection._browserDisconnectedLogs !== undefined)
 | 
				
			||||||
 | 
					      return `Browser closed.` + this._connection._browserDisconnectedLogs;
 | 
				
			||||||
 | 
					    if (this._disposed)
 | 
				
			||||||
 | 
					      return `Target closed`;
 | 
				
			||||||
 | 
					    if (this._connection._closed)
 | 
				
			||||||
 | 
					      return 'Browser closed';
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  async send<T extends keyof Protocol.CommandParameters>(
 | 
					  async send<T extends keyof Protocol.CommandParameters>(
 | 
				
			||||||
    method: T,
 | 
					    method: T,
 | 
				
			||||||
    params?: Protocol.CommandParameters[T]
 | 
					    params?: Protocol.CommandParameters[T]
 | 
				
			||||||
  ): Promise<Protocol.CommandReturnValues[T]> {
 | 
					  ): Promise<Protocol.CommandReturnValues[T]> {
 | 
				
			||||||
    if (this._crashed)
 | 
					    const closedErrorMessage = this._closedErrorMessage();
 | 
				
			||||||
      throw new ProtocolError(true, 'Target crashed');
 | 
					    if (closedErrorMessage)
 | 
				
			||||||
    this._connection._checkClosed(method);
 | 
					      throw new ProtocolError(true, closedErrorMessage);
 | 
				
			||||||
    if (this._disposed)
 | 
					 | 
				
			||||||
      throw new ProtocolError(true, 'Target closed');
 | 
					 | 
				
			||||||
    const id = this._connection.nextMessageId();
 | 
					    const id = this._connection.nextMessageId();
 | 
				
			||||||
    this._rawSend({ method, params, id });
 | 
					    this._rawSend({ method, params, id });
 | 
				
			||||||
    return new Promise((resolve, reject) => {
 | 
					    return new Promise((resolve, reject) => {
 | 
				
			||||||
@ -200,27 +161,30 @@ export class FFSession extends EventEmitter {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  dispatchMessage(object: ProtocolResponse) {
 | 
					  dispatchMessage(object: ProtocolResponse) {
 | 
				
			||||||
    if (object.id && this._callbacks.has(object.id)) {
 | 
					    if (object.id) {
 | 
				
			||||||
      const callback = this._callbacks.get(object.id)!;
 | 
					      const callback = this._callbacks.get(object.id);
 | 
				
			||||||
      this._callbacks.delete(object.id);
 | 
					      // Callbacks could be all rejected if someone has called `.dispose()`.
 | 
				
			||||||
      if (object.error)
 | 
					      if (callback) {
 | 
				
			||||||
        callback.reject(createProtocolError(callback.error, callback.method, object.error));
 | 
					        this._callbacks.delete(object.id);
 | 
				
			||||||
      else
 | 
					        if (object.error)
 | 
				
			||||||
        callback.resolve(object.result);
 | 
					          callback.reject(createProtocolError(callback.error, callback.method, object.error));
 | 
				
			||||||
 | 
					        else
 | 
				
			||||||
 | 
					          callback.resolve(object.result);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      assert(!object.id);
 | 
					 | 
				
			||||||
      Promise.resolve().then(() => this.emit(object.method!, object.params));
 | 
					      Promise.resolve().then(() => this.emit(object.method!, object.params));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  dispose() {
 | 
					  dispose() {
 | 
				
			||||||
    for (const callback of this._callbacks.values()) {
 | 
					 | 
				
			||||||
      callback.error.sessionClosed = true;
 | 
					 | 
				
			||||||
      callback.reject(rewriteErrorMessage(callback.error, 'Target closed'));
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    this._callbacks.clear();
 | 
					 | 
				
			||||||
    this._disposed = true;
 | 
					    this._disposed = true;
 | 
				
			||||||
    this._connection._sessions.delete(this._sessionId);
 | 
					    this._connection._sessions.delete(this._sessionId);
 | 
				
			||||||
 | 
					    const errorMessage = this._closedErrorMessage()!;
 | 
				
			||||||
 | 
					    for (const callback of this._callbacks.values()) {
 | 
				
			||||||
 | 
					      callback.error.sessionClosed = true;
 | 
				
			||||||
 | 
					      callback.reject(rewriteErrorMessage(callback.error, errorMessage));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    this._callbacks.clear();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user