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);
|
||||||
|
// Callbacks could be all rejected if someone has called `.dispose()`.
|
||||||
|
if (callback) {
|
||||||
this._callbacks.delete(object.id);
|
this._callbacks.delete(object.id);
|
||||||
if (object.error)
|
if (object.error)
|
||||||
callback.reject(createProtocolError(callback.error, callback.method, object.error));
|
callback.reject(createProtocolError(callback.error, callback.method, object.error));
|
||||||
else
|
else
|
||||||
callback.resolve(object.result);
|
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