chore: move to generic EventEmitters
This commit is contained in:
parent
1b4d9003c6
commit
7f3c57cc4c
|
|
@ -19,7 +19,7 @@ import type { AddressInfo } from 'net';
|
||||||
import net from 'net';
|
import net from 'net';
|
||||||
import { debugLogger } from '../utils/debugLogger';
|
import { debugLogger } from '../utils/debugLogger';
|
||||||
import { createSocket } from '../utils/happy-eyeballs';
|
import { createSocket } from '../utils/happy-eyeballs';
|
||||||
import { assert, createGuid, } from '../utils';
|
import { assert, createGuid, ManagedEventEmitter, } from '../utils';
|
||||||
|
|
||||||
// https://tools.ietf.org/html/rfc1928
|
// https://tools.ietf.org/html/rfc1928
|
||||||
|
|
||||||
|
|
@ -366,13 +366,11 @@ export function parsePattern(pattern: string | undefined): PatternMatcher {
|
||||||
return (host, port) => matchers.some(matcher => matcher(host, port));
|
return (host, port) => matchers.some(matcher => matcher(host, port));
|
||||||
}
|
}
|
||||||
|
|
||||||
export class SocksProxy extends EventEmitter implements SocksConnectionClient {
|
export class SocksProxy extends ManagedEventEmitter<{
|
||||||
static Events = {
|
'socksRequested': [SocksSocketRequestedPayload],
|
||||||
SocksRequested: 'socksRequested',
|
'socksData': [SocksSocketDataPayload],
|
||||||
SocksData: 'socksData',
|
'socksClosed': [SocksSocketClosedPayload],
|
||||||
SocksClosed: 'socksClosed',
|
}> implements SocksConnectionClient {
|
||||||
};
|
|
||||||
|
|
||||||
private _server: net.Server;
|
private _server: net.Server;
|
||||||
private _connections = new Map<string, SocksConnection>();
|
private _connections = new Map<string, SocksConnection>();
|
||||||
private _sockets = new Set<net.Socket>();
|
private _sockets = new Set<net.Socket>();
|
||||||
|
|
@ -456,7 +454,7 @@ export class SocksProxy extends EventEmitter implements SocksConnectionClient {
|
||||||
this._handleDirect(payload);
|
this._handleDirect(payload);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.emit(SocksProxy.Events.SocksRequested, payload);
|
this.emit('socksRequested', payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
onSocketData(payload: SocksSocketDataPayload): void {
|
onSocketData(payload: SocksSocketDataPayload): void {
|
||||||
|
|
@ -465,7 +463,7 @@ export class SocksProxy extends EventEmitter implements SocksConnectionClient {
|
||||||
direct.write(payload.data);
|
direct.write(payload.data);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.emit(SocksProxy.Events.SocksData, payload);
|
this.emit('socksData', payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
onSocketClosed(payload: SocksSocketClosedPayload): void {
|
onSocketClosed(payload: SocksSocketClosedPayload): void {
|
||||||
|
|
@ -475,7 +473,7 @@ export class SocksProxy extends EventEmitter implements SocksConnectionClient {
|
||||||
this._directSockets.delete(payload.uid);
|
this._directSockets.delete(payload.uid);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.emit(SocksProxy.Events.SocksClosed, payload);
|
this.emit('socksClosed', payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
socketConnected({ uid, host, port }: SocksSocketConnectedPayload) {
|
socketConnected({ uid, host, port }: SocksSocketConnectedPayload) {
|
||||||
|
|
@ -499,15 +497,13 @@ export class SocksProxy extends EventEmitter implements SocksConnectionClient {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class SocksProxyHandler extends EventEmitter {
|
export class SocksProxyHandler extends EventEmitter<{
|
||||||
static Events = {
|
socksConnected: [SocksSocketConnectedPayload],
|
||||||
SocksConnected: 'socksConnected',
|
socksData: [SocksSocketDataPayload],
|
||||||
SocksData: 'socksData',
|
socksError: [SocksSocketErrorPayload],
|
||||||
SocksError: 'socksError',
|
socksFailed: [SocksSocketFailedPayload],
|
||||||
SocksFailed: 'socksFailed',
|
socksEnd: [SocksSocketEndPayload],
|
||||||
SocksEnd: 'socksEnd',
|
}> {
|
||||||
};
|
|
||||||
|
|
||||||
private _sockets = new Map<string, net.Socket>();
|
private _sockets = new Map<string, net.Socket>();
|
||||||
private _patternMatcher: PatternMatcher = () => false;
|
private _patternMatcher: PatternMatcher = () => false;
|
||||||
private _redirectPortForTest: number | undefined;
|
private _redirectPortForTest: number | undefined;
|
||||||
|
|
@ -528,7 +524,7 @@ export class SocksProxyHandler extends EventEmitter {
|
||||||
if (!this._patternMatcher(host, port)) {
|
if (!this._patternMatcher(host, port)) {
|
||||||
const payload: SocksSocketFailedPayload = { uid, errorCode: 'ERULESET' };
|
const payload: SocksSocketFailedPayload = { uid, errorCode: 'ERULESET' };
|
||||||
debugLogger.log('socks', `[${uid}] <= pattern error ${payload.errorCode}`);
|
debugLogger.log('socks', `[${uid}] <= pattern error ${payload.errorCode}`);
|
||||||
this.emit(SocksProxyHandler.Events.SocksFailed, payload);
|
this.emit('socksFailed', payload);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -540,18 +536,18 @@ export class SocksProxyHandler extends EventEmitter {
|
||||||
const socket = await createSocket(host, port);
|
const socket = await createSocket(host, port);
|
||||||
socket.on('data', data => {
|
socket.on('data', data => {
|
||||||
const payload: SocksSocketDataPayload = { uid, data };
|
const payload: SocksSocketDataPayload = { uid, data };
|
||||||
this.emit(SocksProxyHandler.Events.SocksData, payload);
|
this.emit('socksData', payload);
|
||||||
});
|
});
|
||||||
socket.on('error', error => {
|
socket.on('error', error => {
|
||||||
const payload: SocksSocketErrorPayload = { uid, error: error.message };
|
const payload: SocksSocketErrorPayload = { uid, error: error.message };
|
||||||
debugLogger.log('socks', `[${uid}] <= network socket error ${payload.error}`);
|
debugLogger.log('socks', `[${uid}] <= network socket error ${payload.error}`);
|
||||||
this.emit(SocksProxyHandler.Events.SocksError, payload);
|
this.emit('socksError', payload);
|
||||||
this._sockets.delete(uid);
|
this._sockets.delete(uid);
|
||||||
});
|
});
|
||||||
socket.on('end', () => {
|
socket.on('end', () => {
|
||||||
const payload: SocksSocketEndPayload = { uid };
|
const payload: SocksSocketEndPayload = { uid };
|
||||||
debugLogger.log('socks', `[${uid}] <= network socket closed`);
|
debugLogger.log('socks', `[${uid}] <= network socket closed`);
|
||||||
this.emit(SocksProxyHandler.Events.SocksEnd, payload);
|
this.emit('socksEnd', payload);
|
||||||
this._sockets.delete(uid);
|
this._sockets.delete(uid);
|
||||||
});
|
});
|
||||||
const localAddress = socket.localAddress;
|
const localAddress = socket.localAddress;
|
||||||
|
|
@ -559,11 +555,11 @@ export class SocksProxyHandler extends EventEmitter {
|
||||||
this._sockets.set(uid, socket);
|
this._sockets.set(uid, socket);
|
||||||
const payload: SocksSocketConnectedPayload = { uid, host: localAddress!, port: localPort! };
|
const payload: SocksSocketConnectedPayload = { uid, host: localAddress!, port: localPort! };
|
||||||
debugLogger.log('socks', `[${uid}] <= connected to network ${payload.host}:${payload.port}`);
|
debugLogger.log('socks', `[${uid}] <= connected to network ${payload.host}:${payload.port}`);
|
||||||
this.emit(SocksProxyHandler.Events.SocksConnected, payload);
|
this.emit('socksConnected', payload);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const payload: SocksSocketFailedPayload = { uid, errorCode: error.code };
|
const payload: SocksSocketFailedPayload = { uid, errorCode: error.code };
|
||||||
debugLogger.log('socks', `[${uid}] <= connect error ${payload.errorCode}`);
|
debugLogger.log('socks', `[${uid}] <= connect error ${payload.errorCode}`);
|
||||||
this.emit(SocksProxyHandler.Events.SocksFailed, payload);
|
this.emit('socksFailed', payload);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,12 +18,12 @@ import type { WebSocket } from '../utilsBundle';
|
||||||
import type { DispatcherScope, Playwright } from '../server';
|
import type { DispatcherScope, Playwright } from '../server';
|
||||||
import type * as channels from '@protocol/channels';
|
import type * as channels from '@protocol/channels';
|
||||||
import { createPlaywright, DispatcherConnection, RootDispatcher, PlaywrightDispatcher } from '../server';
|
import { createPlaywright, DispatcherConnection, RootDispatcher, PlaywrightDispatcher } from '../server';
|
||||||
import { Browser } from '../server/browser';
|
import type { Browser } from '../server/browser';
|
||||||
import { serverSideCallMetadata } from '../server/instrumentation';
|
import { serverSideCallMetadata } from '../server/instrumentation';
|
||||||
import { SocksProxy } from '../common/socksProxy';
|
import { SocksProxy } from '../common/socksProxy';
|
||||||
import { assert, isUnderTest } from '../utils';
|
import { assert, isUnderTest } from '../utils';
|
||||||
import type { LaunchOptions } from '../server/types';
|
import type { LaunchOptions } from '../server/types';
|
||||||
import { AndroidDevice } from '../server/android/android';
|
import type { AndroidDevice } from '../server/android/android';
|
||||||
import { DebugControllerDispatcher } from '../server/dispatchers/debugControllerDispatcher';
|
import { DebugControllerDispatcher } from '../server/dispatchers/debugControllerDispatcher';
|
||||||
import { startProfiling, stopProfiling } from '../utils';
|
import { startProfiling, stopProfiling } from '../utils';
|
||||||
import { monotonicTime } from '../utils';
|
import { monotonicTime } from '../utils';
|
||||||
|
|
@ -123,7 +123,7 @@ export class PlaywrightConnection {
|
||||||
for (const browser of playwright.allBrowsers())
|
for (const browser of playwright.allBrowsers())
|
||||||
await browser.close({ reason: 'Connection terminated' });
|
await browser.close({ reason: 'Connection terminated' });
|
||||||
});
|
});
|
||||||
browser.on(Browser.Events.Disconnected, () => {
|
browser.on('disconnected', () => {
|
||||||
// Underlying browser did close for some reason - force disconnect the client.
|
// Underlying browser did close for some reason - force disconnect the client.
|
||||||
this.close({ code: 1001, reason: 'Browser closed' });
|
this.close({ code: 1001, reason: 'Browser closed' });
|
||||||
});
|
});
|
||||||
|
|
@ -139,7 +139,7 @@ export class PlaywrightConnection {
|
||||||
this._preLaunched.socksProxy?.setPattern(this._options.socksProxyPattern);
|
this._preLaunched.socksProxy?.setPattern(this._options.socksProxyPattern);
|
||||||
|
|
||||||
const browser = this._preLaunched.browser!;
|
const browser = this._preLaunched.browser!;
|
||||||
browser.on(Browser.Events.Disconnected, () => {
|
browser.on('disconnected', () => {
|
||||||
// Underlying browser did close for some reason - force disconnect the client.
|
// Underlying browser did close for some reason - force disconnect the client.
|
||||||
this.close({ code: 1001, reason: 'Browser closed' });
|
this.close({ code: 1001, reason: 'Browser closed' });
|
||||||
});
|
});
|
||||||
|
|
@ -158,7 +158,7 @@ export class PlaywrightConnection {
|
||||||
debugLogger.log('server', `[${this._id}] engaged pre-launched (Android) mode`);
|
debugLogger.log('server', `[${this._id}] engaged pre-launched (Android) mode`);
|
||||||
const playwright = this._preLaunched.playwright!;
|
const playwright = this._preLaunched.playwright!;
|
||||||
const androidDevice = this._preLaunched.androidDevice!;
|
const androidDevice = this._preLaunched.androidDevice!;
|
||||||
androidDevice.on(AndroidDevice.Events.Close, () => {
|
androidDevice.on('close', () => {
|
||||||
// Underlying browser did close for some reason - force disconnect the client.
|
// Underlying browser did close for some reason - force disconnect the client.
|
||||||
this.close({ code: 1001, reason: 'Android device disconnected' });
|
this.close({ code: 1001, reason: 'Android device disconnected' });
|
||||||
});
|
});
|
||||||
|
|
@ -202,7 +202,7 @@ export class PlaywrightConnection {
|
||||||
...this._options.launchOptions,
|
...this._options.launchOptions,
|
||||||
headless: !!process.env.PW_DEBUG_CONTROLLER_HEADLESS,
|
headless: !!process.env.PW_DEBUG_CONTROLLER_HEADLESS,
|
||||||
});
|
});
|
||||||
browser.on(Browser.Events.Disconnected, () => {
|
browser.on('disconnected', () => {
|
||||||
// Underlying browser did close for some reason - force disconnect the client.
|
// Underlying browser did close for some reason - force disconnect the client.
|
||||||
this.close({ code: 1001, reason: 'Browser closed' });
|
this.close({ code: 1001, reason: 'Browser closed' });
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ import os from 'os';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import type * as stream from 'stream';
|
import type * as stream from 'stream';
|
||||||
import { wsReceiver, wsSender } from '../../utilsBundle';
|
import { wsReceiver, wsSender } from '../../utilsBundle';
|
||||||
|
import type { ManagedEventEmitter } from '../../utils';
|
||||||
import { createGuid, makeWaitForNextTask, isUnderTest, getPackageManagerExecCommand } from '../../utils';
|
import { createGuid, makeWaitForNextTask, isUnderTest, getPackageManagerExecCommand } from '../../utils';
|
||||||
import { removeFolders } from '../../utils/fileUtils';
|
import { removeFolders } from '../../utils/fileUtils';
|
||||||
import type { BrowserOptions, BrowserProcess } from '../browser';
|
import type { BrowserOptions, BrowserProcess } from '../browser';
|
||||||
|
|
@ -53,7 +54,13 @@ export interface DeviceBackend {
|
||||||
open(command: string): Promise<SocketBackend>;
|
open(command: string): Promise<SocketBackend>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SocketBackend extends EventEmitter {
|
export type SocketBackendEvents = {
|
||||||
|
data: [Buffer],
|
||||||
|
close: []
|
||||||
|
error: [Error]
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface SocketBackend extends ManagedEventEmitter<SocketBackendEvents> {
|
||||||
guid: string;
|
guid: string;
|
||||||
write(data: Buffer): Promise<void>;
|
write(data: Buffer): Promise<void>;
|
||||||
close(): void;
|
close(): void;
|
||||||
|
|
@ -96,7 +103,11 @@ export class Android extends SdkObject {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class AndroidDevice extends SdkObject {
|
export class AndroidDevice extends SdkObject<{
|
||||||
|
webViewAdded: [channels.AndroidWebView]
|
||||||
|
webViewRemoved: [string]
|
||||||
|
close: []
|
||||||
|
}> {
|
||||||
readonly _backend: DeviceBackend;
|
readonly _backend: DeviceBackend;
|
||||||
readonly model: string;
|
readonly model: string;
|
||||||
readonly serial: string;
|
readonly serial: string;
|
||||||
|
|
@ -108,12 +119,6 @@ export class AndroidDevice extends SdkObject {
|
||||||
readonly _timeoutSettings: TimeoutSettings;
|
readonly _timeoutSettings: TimeoutSettings;
|
||||||
private _webViews = new Map<string, channels.AndroidWebView>();
|
private _webViews = new Map<string, channels.AndroidWebView>();
|
||||||
|
|
||||||
static Events = {
|
|
||||||
WebViewAdded: 'webViewAdded',
|
|
||||||
WebViewRemoved: 'webViewRemoved',
|
|
||||||
Close: 'close',
|
|
||||||
};
|
|
||||||
|
|
||||||
private _browserConnections = new Set<AndroidBrowser>();
|
private _browserConnections = new Set<AndroidBrowser>();
|
||||||
readonly _android: Android;
|
readonly _android: Android;
|
||||||
private _isClosed = false;
|
private _isClosed = false;
|
||||||
|
|
@ -256,7 +261,7 @@ export class AndroidDevice extends SdkObject {
|
||||||
}
|
}
|
||||||
await this._backend.close();
|
await this._backend.close();
|
||||||
this._android._deviceClosed(this);
|
this._android._deviceClosed(this);
|
||||||
this.emit(AndroidDevice.Events.Close);
|
this.emit('close');
|
||||||
}
|
}
|
||||||
|
|
||||||
async launchBrowser(pkg: string = 'com.android.chrome', options: channels.AndroidDeviceLaunchBrowserParams): Promise<BrowserContext> {
|
async launchBrowser(pkg: string = 'com.android.chrome', options: channels.AndroidDeviceLaunchBrowserParams): Promise<BrowserContext> {
|
||||||
|
|
@ -420,12 +425,12 @@ export class AndroidDevice extends SdkObject {
|
||||||
|
|
||||||
const webView = { pid, pkg, socketName };
|
const webView = { pid, pkg, socketName };
|
||||||
this._webViews.set(socketName, webView);
|
this._webViews.set(socketName, webView);
|
||||||
this.emit(AndroidDevice.Events.WebViewAdded, webView);
|
this.emit('webViewAdded', webView);
|
||||||
}
|
}
|
||||||
for (const p of this._webViews.keys()) {
|
for (const p of this._webViews.keys()) {
|
||||||
if (!socketNames.has(p)) {
|
if (!socketNames.has(p)) {
|
||||||
this._webViews.delete(p);
|
this._webViews.delete(p);
|
||||||
this.emit(AndroidDevice.Events.WebViewRemoved, p);
|
this.emit('webViewRemoved', p);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,9 +17,8 @@
|
||||||
import { debug } from '../../utilsBundle';
|
import { debug } from '../../utilsBundle';
|
||||||
import type * as channels from '@protocol/channels';
|
import type * as channels from '@protocol/channels';
|
||||||
import * as net from 'net';
|
import * as net from 'net';
|
||||||
import { EventEmitter } from 'events';
|
import type { Backend, DeviceBackend, SocketBackend, SocketBackendEvents } from './android';
|
||||||
import type { Backend, DeviceBackend, SocketBackend } from './android';
|
import { assert, createGuid, ManagedEventEmitter } from '../../utils';
|
||||||
import { assert, createGuid } from '../../utils';
|
|
||||||
|
|
||||||
export class AdbBackend implements Backend {
|
export class AdbBackend implements Backend {
|
||||||
async devices(options: channels.AndroidDevicesOptions = {}): Promise<DeviceBackend[]> {
|
async devices(options: channels.AndroidDevicesOptions = {}): Promise<DeviceBackend[]> {
|
||||||
|
|
@ -112,7 +111,7 @@ function encodeMessage(message: string): Buffer {
|
||||||
return Buffer.from(lenHex + message);
|
return Buffer.from(lenHex + message);
|
||||||
}
|
}
|
||||||
|
|
||||||
class BufferedSocketWrapper extends EventEmitter implements SocketBackend {
|
class BufferedSocketWrapper extends ManagedEventEmitter<SocketBackendEvents> implements SocketBackend {
|
||||||
readonly guid = createGuid();
|
readonly guid = createGuid();
|
||||||
private _socket: net.Socket;
|
private _socket: net.Socket;
|
||||||
private _buffer = Buffer.from([]);
|
private _buffer = Buffer.from([]);
|
||||||
|
|
|
||||||
|
|
@ -52,13 +52,10 @@ export type BrowserOptions = {
|
||||||
originalLaunchOptions: types.LaunchOptions;
|
originalLaunchOptions: types.LaunchOptions;
|
||||||
};
|
};
|
||||||
|
|
||||||
export abstract class Browser extends SdkObject {
|
export abstract class Browser extends SdkObject<{
|
||||||
|
beforeClose: []
|
||||||
static Events = {
|
disconnected: []
|
||||||
BeforeClose: 'beforeClose',
|
}> {
|
||||||
Disconnected: 'disconnected',
|
|
||||||
};
|
|
||||||
|
|
||||||
readonly options: BrowserOptions;
|
readonly options: BrowserOptions;
|
||||||
private _downloads = new Map<string, Download>();
|
private _downloads = new Map<string, Download>();
|
||||||
_defaultContext: BrowserContext | null = null;
|
_defaultContext: BrowserContext | null = null;
|
||||||
|
|
@ -140,8 +137,8 @@ export abstract class Browser extends SdkObject {
|
||||||
pageOrError.then(page => {
|
pageOrError.then(page => {
|
||||||
if (page instanceof Page) {
|
if (page instanceof Page) {
|
||||||
page._video = artifact;
|
page._video = artifact;
|
||||||
page.emitOnContext(BrowserContext.Events.VideoStarted, artifact);
|
page.emitOnContext('videostarted', artifact);
|
||||||
page.emit(Page.Events.Video, artifact);
|
page.emit('video', artifact);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -158,7 +155,7 @@ export abstract class Browser extends SdkObject {
|
||||||
|
|
||||||
_didClose() {
|
_didClose() {
|
||||||
if (this._needsBeforeCloseEvent)
|
if (this._needsBeforeCloseEvent)
|
||||||
this.emit(Browser.Events.BeforeClose);
|
this.emit('beforeClose');
|
||||||
else
|
else
|
||||||
this.beforeCloseFinished();
|
this.beforeCloseFinished();
|
||||||
}
|
}
|
||||||
|
|
@ -168,7 +165,7 @@ export abstract class Browser extends SdkObject {
|
||||||
context._browserClosed();
|
context._browserClosed();
|
||||||
if (this._defaultContext)
|
if (this._defaultContext)
|
||||||
this._defaultContext._browserClosed();
|
this._defaultContext._browserClosed();
|
||||||
this.emit(Browser.Events.Disconnected);
|
this.emit('disconnected');
|
||||||
this.instrumentation.onBrowserClose(this);
|
this.instrumentation.onBrowserClose(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -180,7 +177,7 @@ export abstract class Browser extends SdkObject {
|
||||||
await this.options.browserProcess.close();
|
await this.options.browserProcess.close();
|
||||||
}
|
}
|
||||||
if (this.isConnected())
|
if (this.isConnected())
|
||||||
await new Promise(x => this.once(Browser.Events.Disconnected, x));
|
await new Promise<void>(x => this.once('disconnected', x));
|
||||||
}
|
}
|
||||||
|
|
||||||
async killForTests() {
|
async killForTests() {
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ import type * as frames from './frames';
|
||||||
import { helper } from './helper';
|
import { helper } from './helper';
|
||||||
import * as network from './network';
|
import * as network from './network';
|
||||||
import { InitScript } from './page';
|
import { InitScript } from './page';
|
||||||
import type { PageDelegate } from './page';
|
import type { PageDelegate, Worker } from './page';
|
||||||
import { Page, PageBinding } from './page';
|
import { Page, PageBinding } from './page';
|
||||||
import type { Progress, ProgressController } from './progress';
|
import type { Progress, ProgressController } from './progress';
|
||||||
import type { Selectors } from './selectors';
|
import type { Selectors } from './selectors';
|
||||||
|
|
@ -44,27 +44,33 @@ import { BrowserContextAPIRequestContext } from './fetch';
|
||||||
import type { Artifact } from './artifact';
|
import type { Artifact } from './artifact';
|
||||||
import { Clock } from './clock';
|
import { Clock } from './clock';
|
||||||
import { ClientCertificatesProxy } from './socksClientCertificatesInterceptor';
|
import { ClientCertificatesProxy } from './socksClientCertificatesInterceptor';
|
||||||
|
import type { ConsoleMessage } from './console';
|
||||||
|
import type { Dialog } from './dialog';
|
||||||
|
|
||||||
export abstract class BrowserContext extends SdkObject {
|
export type BrowserContextEvents = {
|
||||||
static Events = {
|
console: [ConsoleMessage]
|
||||||
Console: 'console',
|
close: []
|
||||||
Close: 'close',
|
dialog: [Dialog]
|
||||||
Dialog: 'dialog',
|
page: [Page]
|
||||||
Page: 'page',
|
// Can't use just 'error' due to node.js special treatment of error events.
|
||||||
// Can't use just 'error' due to node.js special treatment of error events.
|
// @see https://nodejs.org/api/events.html#events_error_events
|
||||||
// @see https://nodejs.org/api/events.html#events_error_events
|
pageerror: [Error, Page]
|
||||||
PageError: 'pageerror',
|
request: [network.Request]
|
||||||
Request: 'request',
|
response: [network.Response]
|
||||||
Response: 'response',
|
requestfailed: [network.Request]
|
||||||
RequestFailed: 'requestfailed',
|
requestfinished: [{ request: network.Request, response: network.Response | null}]
|
||||||
RequestFinished: 'requestfinished',
|
requestaborted: [network.Request]
|
||||||
RequestAborted: 'requestaborted',
|
requestfulfilled: [network.Request]
|
||||||
RequestFulfilled: 'requestfulfilled',
|
requestcontinued: [network.Request]
|
||||||
RequestContinued: 'requestcontinued',
|
beforeclose: []
|
||||||
BeforeClose: 'beforeclose',
|
videostarted: [Artifact]
|
||||||
VideoStarted: 'videostarted',
|
|
||||||
};
|
|
||||||
|
|
||||||
|
// Chromium only
|
||||||
|
backgroundpage: [Page]
|
||||||
|
serviceworker: [Worker]
|
||||||
|
};
|
||||||
|
|
||||||
|
export abstract class BrowserContext extends SdkObject<BrowserContextEvents> {
|
||||||
readonly _timeoutSettings = new TimeoutSettings();
|
readonly _timeoutSettings = new TimeoutSettings();
|
||||||
readonly _pageBindings = new Map<string, PageBinding>();
|
readonly _pageBindings = new Map<string, PageBinding>();
|
||||||
readonly _activeProgressControllers = new Set<ProgressController>();
|
readonly _activeProgressControllers = new Set<ProgressController>();
|
||||||
|
|
@ -136,7 +142,7 @@ export abstract class BrowserContext extends SdkObject {
|
||||||
// When paused, show inspector.
|
// When paused, show inspector.
|
||||||
if (this._debugger.isPaused())
|
if (this._debugger.isPaused())
|
||||||
Recorder.showInspector(this);
|
Recorder.showInspector(this);
|
||||||
this._debugger.on(Debugger.Events.PausedStateChanged, () => {
|
this._debugger.on('pausedstatechanged', () => {
|
||||||
Recorder.showInspector(this);
|
Recorder.showInspector(this);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -252,7 +258,7 @@ export abstract class BrowserContext extends SdkObject {
|
||||||
if (this._isPersistentContext)
|
if (this._isPersistentContext)
|
||||||
this.onClosePersistent();
|
this.onClosePersistent();
|
||||||
this._closePromiseFulfill!(new Error('Context closed'));
|
this._closePromiseFulfill!(new Error('Context closed'));
|
||||||
this.emit(BrowserContext.Events.Close);
|
this.emit('close');
|
||||||
}
|
}
|
||||||
|
|
||||||
// BrowserContext methods.
|
// BrowserContext methods.
|
||||||
|
|
@ -359,7 +365,7 @@ export abstract class BrowserContext extends SdkObject {
|
||||||
|
|
||||||
async _loadDefaultContextAsIs(progress: Progress): Promise<Page[]> {
|
async _loadDefaultContextAsIs(progress: Progress): Promise<Page[]> {
|
||||||
if (!this.pages().length) {
|
if (!this.pages().length) {
|
||||||
const waitForEvent = helper.waitForEvent(progress, this, BrowserContext.Events.Page);
|
const waitForEvent = helper.waitForEvent(progress, this, 'page');
|
||||||
progress.cleanupWhenAborted(() => waitForEvent.dispose);
|
progress.cleanupWhenAborted(() => waitForEvent.dispose);
|
||||||
const page = (await waitForEvent.promise) as Page;
|
const page = (await waitForEvent.promise) as Page;
|
||||||
if (page._pageIsError)
|
if (page._pageIsError)
|
||||||
|
|
@ -443,7 +449,7 @@ export abstract class BrowserContext extends SdkObject {
|
||||||
if (this._closedStatus === 'open') {
|
if (this._closedStatus === 'open') {
|
||||||
if (options.reason)
|
if (options.reason)
|
||||||
this._closeReason = options.reason;
|
this._closeReason = options.reason;
|
||||||
this.emit(BrowserContext.Events.BeforeClose);
|
this.emit('beforeclose');
|
||||||
this._closedStatus = 'closing';
|
this._closedStatus = 'closing';
|
||||||
|
|
||||||
for (const harRecorder of this._harRecorders.values())
|
for (const harRecorder of this._harRecorders.values())
|
||||||
|
|
@ -615,10 +621,10 @@ export abstract class BrowserContext extends SdkObject {
|
||||||
async extendInjectedScript(source: string, arg?: any) {
|
async extendInjectedScript(source: string, arg?: any) {
|
||||||
const installInFrame = (frame: frames.Frame) => frame.extendInjectedScript(source, arg).catch(() => {});
|
const installInFrame = (frame: frames.Frame) => frame.extendInjectedScript(source, arg).catch(() => {});
|
||||||
const installInPage = (page: Page) => {
|
const installInPage = (page: Page) => {
|
||||||
page.on(Page.Events.InternalFrameNavigatedToNewDocument, installInFrame);
|
page.on('internalframenavigatedtonewdocument', installInFrame);
|
||||||
return Promise.all(page.frames().map(installInFrame));
|
return Promise.all(page.frames().map(installInFrame));
|
||||||
};
|
};
|
||||||
this.on(BrowserContext.Events.Page, installInPage);
|
this.on('page', installInPage);
|
||||||
return Promise.all(this.pages().map(installInPage));
|
return Promise.all(this.pages().map(installInPage));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,6 @@ import type { ConnectionTransport, ProtocolRequest } from '../transport';
|
||||||
import { WebSocketTransport } from '../transport';
|
import { WebSocketTransport } from '../transport';
|
||||||
import { CRDevTools } from './crDevTools';
|
import { CRDevTools } from './crDevTools';
|
||||||
import type { BrowserOptions, BrowserProcess } from '../browser';
|
import type { BrowserOptions, BrowserProcess } from '../browser';
|
||||||
import { Browser } from '../browser';
|
|
||||||
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 type { HTTPRequestParams } from '../../utils/network';
|
import type { HTTPRequestParams } from '../../utils/network';
|
||||||
|
|
@ -121,7 +120,7 @@ export class Chromium extends BrowserType {
|
||||||
progress.throwIfAborted();
|
progress.throwIfAborted();
|
||||||
const browser = await CRBrowser.connect(this.attribution.playwright, chromeTransport, browserOptions);
|
const browser = await CRBrowser.connect(this.attribution.playwright, chromeTransport, browserOptions);
|
||||||
browser._isCollocatedWithServer = false;
|
browser._isCollocatedWithServer = false;
|
||||||
browser.on(Browser.Events.Disconnected, doCleanup);
|
browser.on('disconnected', doCleanup);
|
||||||
return browser;
|
return browser;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -192,7 +192,7 @@ export class CRBrowser extends Browser {
|
||||||
if (targetInfo.type === 'service_worker') {
|
if (targetInfo.type === 'service_worker') {
|
||||||
const serviceWorker = new CRServiceWorker(context, session, targetInfo.url);
|
const serviceWorker = new CRServiceWorker(context, session, targetInfo.url);
|
||||||
this._serviceWorkers.set(targetInfo.targetId, serviceWorker);
|
this._serviceWorkers.set(targetInfo.targetId, serviceWorker);
|
||||||
context.emit(CRBrowserContext.CREvents.ServiceWorker, serviceWorker);
|
context.emit('serviceworker', serviceWorker);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -333,11 +333,6 @@ export class CRBrowser extends Browser {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class CRBrowserContext extends BrowserContext {
|
export class CRBrowserContext extends BrowserContext {
|
||||||
static CREvents = {
|
|
||||||
BackgroundPage: 'backgroundpage',
|
|
||||||
ServiceWorker: 'serviceworker',
|
|
||||||
};
|
|
||||||
|
|
||||||
declare readonly _browser: CRBrowser;
|
declare readonly _browser: CRBrowser;
|
||||||
|
|
||||||
constructor(browser: CRBrowser, browserContextId: string | undefined, options: channels.BrowserNewContextParams) {
|
constructor(browser: CRBrowser, browserContextId: string | undefined, options: channels.BrowserNewContextParams) {
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { type RegisteredListener, assert, eventsHelper } from '../../utils';
|
import { ManagedEventEmitter, type RegisteredListener, assert, eventsHelper } 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 { EventEmitter } from 'events';
|
import { EventEmitter } from 'events';
|
||||||
|
|
@ -97,7 +97,7 @@ export class CRConnection extends EventEmitter {
|
||||||
|
|
||||||
type SessionEventListener = (method: string, params?: Object) => void;
|
type SessionEventListener = (method: string, params?: Object) => void;
|
||||||
|
|
||||||
export class CRSession extends EventEmitter {
|
export class CRSession extends ManagedEventEmitter<{ [K in keyof Protocol.Events]: [Protocol.Events[K]] }> {
|
||||||
private readonly _connection: CRConnection;
|
private readonly _connection: CRConnection;
|
||||||
private _eventListener?: SessionEventListener;
|
private _eventListener?: SessionEventListener;
|
||||||
private readonly _callbacks = new Map<number, { resolve: (o: any) => void, reject: (e: ProtocolError) => void, error: ProtocolError }>();
|
private readonly _callbacks = new Map<number, { resolve: (o: any) => void, reject: (e: ProtocolError) => void, error: ProtocolError }>();
|
||||||
|
|
@ -105,11 +105,6 @@ export class CRSession extends EventEmitter {
|
||||||
private readonly _parentSession: CRSession | null;
|
private readonly _parentSession: CRSession | null;
|
||||||
private _crashed: boolean = false;
|
private _crashed: boolean = false;
|
||||||
private _closed = false;
|
private _closed = false;
|
||||||
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(connection: CRConnection, parentSession: CRSession | null, sessionId: string, eventListener?: SessionEventListener) {
|
constructor(connection: CRConnection, parentSession: CRSession | null, sessionId: string, eventListener?: SessionEventListener) {
|
||||||
super();
|
super();
|
||||||
|
|
@ -169,6 +164,7 @@ export class CRSession extends EventEmitter {
|
||||||
Promise.resolve().then(() => {
|
Promise.resolve().then(() => {
|
||||||
if (this._eventListener)
|
if (this._eventListener)
|
||||||
this._eventListener(object.method!, object.params);
|
this._eventListener(object.method!, object.params);
|
||||||
|
// @ts-ignore
|
||||||
this.emit(object.method!, object.params);
|
this.emit(object.method!, object.params);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -199,12 +195,10 @@ export class CRSession extends EventEmitter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class CDPSession extends EventEmitter {
|
export class CDPSession extends ManagedEventEmitter<{
|
||||||
static Events = {
|
event: [{ method: string, params: Object | undefined }]
|
||||||
Event: 'event',
|
closed: [];
|
||||||
Closed: 'close',
|
}> {
|
||||||
};
|
|
||||||
|
|
||||||
readonly guid: string;
|
readonly guid: string;
|
||||||
private _session: CRSession;
|
private _session: CRSession;
|
||||||
private _listeners: RegisteredListener[] = [];
|
private _listeners: RegisteredListener[] = [];
|
||||||
|
|
@ -212,8 +206,8 @@ export class CDPSession extends EventEmitter {
|
||||||
constructor(parentSession: CRSession, sessionId: string) {
|
constructor(parentSession: CRSession, sessionId: string) {
|
||||||
super();
|
super();
|
||||||
this.guid = `cdp-session@${sessionId}`;
|
this.guid = `cdp-session@${sessionId}`;
|
||||||
this._session = parentSession.createChildSession(sessionId, (method, params) => this.emit(CDPSession.Events.Event, { method, params }));
|
this._session = parentSession.createChildSession(sessionId, (method, params) => this.emit('event', { method, params }));
|
||||||
this._listeners = [eventsHelper.addEventListener(parentSession, 'Target.detachedFromTarget', (event: Protocol.Target.detachedFromTargetPayload) => {
|
this._listeners = [parentSession.addManagedListener('Target.detachedFromTarget', (event: Protocol.Target.detachedFromTargetPayload) => {
|
||||||
if (event.sessionId === sessionId)
|
if (event.sessionId === sessionId)
|
||||||
this._onClose();
|
this._onClose();
|
||||||
})];
|
})];
|
||||||
|
|
@ -235,6 +229,6 @@ export class CDPSession extends EventEmitter {
|
||||||
private _onClose() {
|
private _onClose() {
|
||||||
eventsHelper.removeEventListeners(this._listeners);
|
eventsHelper.removeEventListeners(this._listeners);
|
||||||
this._session.dispose();
|
this._session.dispose();
|
||||||
this.emit(CDPSession.Events.Closed);
|
this.emit('closed');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -78,9 +78,9 @@ class JSCoverage {
|
||||||
this._scriptIds.clear();
|
this._scriptIds.clear();
|
||||||
this._scriptSources.clear();
|
this._scriptSources.clear();
|
||||||
this._eventListeners = [
|
this._eventListeners = [
|
||||||
eventsHelper.addEventListener(this._client, 'Debugger.scriptParsed', this._onScriptParsed.bind(this)),
|
this._client.addManagedListener('Debugger.scriptParsed', this._onScriptParsed.bind(this)),
|
||||||
eventsHelper.addEventListener(this._client, 'Runtime.executionContextsCleared', this._onExecutionContextsCleared.bind(this)),
|
this._client.addManagedListener('Runtime.executionContextsCleared', this._onExecutionContextsCleared.bind(this)),
|
||||||
eventsHelper.addEventListener(this._client, 'Debugger.paused', this._onDebuggerPaused.bind(this)),
|
this._client.addManagedListener('Debugger.paused', this._onDebuggerPaused.bind(this)),
|
||||||
];
|
];
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
this._client.send('Profiler.enable'),
|
this._client.send('Profiler.enable'),
|
||||||
|
|
@ -164,8 +164,8 @@ class CSSCoverage {
|
||||||
this._stylesheetURLs.clear();
|
this._stylesheetURLs.clear();
|
||||||
this._stylesheetSources.clear();
|
this._stylesheetSources.clear();
|
||||||
this._eventListeners = [
|
this._eventListeners = [
|
||||||
eventsHelper.addEventListener(this._client, 'CSS.styleSheetAdded', this._onStyleSheet.bind(this)),
|
this._client.addManagedListener('CSS.styleSheetAdded', this._onStyleSheet.bind(this)),
|
||||||
eventsHelper.addEventListener(this._client, 'Runtime.executionContextsCleared', this._onExecutionContextsCleared.bind(this)),
|
this._client.addManagedListener('Runtime.executionContextsCleared', this._onExecutionContextsCleared.bind(this)),
|
||||||
];
|
];
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
this._client.send('DOM.enable'),
|
this._client.send('DOM.enable'),
|
||||||
|
|
|
||||||
|
|
@ -60,25 +60,25 @@ export class CRNetworkManager {
|
||||||
async addSession(session: CRSession, workerFrame?: frames.Frame, isMain?: boolean) {
|
async addSession(session: CRSession, workerFrame?: frames.Frame, isMain?: boolean) {
|
||||||
const sessionInfo: SessionInfo = { session, isMain, workerFrame, eventListeners: [] };
|
const sessionInfo: SessionInfo = { session, isMain, workerFrame, eventListeners: [] };
|
||||||
sessionInfo.eventListeners = [
|
sessionInfo.eventListeners = [
|
||||||
eventsHelper.addEventListener(session, 'Fetch.requestPaused', this._onRequestPaused.bind(this, sessionInfo)),
|
session.addManagedListener('Fetch.requestPaused', this._onRequestPaused.bind(this, sessionInfo)),
|
||||||
eventsHelper.addEventListener(session, 'Fetch.authRequired', this._onAuthRequired.bind(this, sessionInfo)),
|
session.addManagedListener('Fetch.authRequired', this._onAuthRequired.bind(this, sessionInfo)),
|
||||||
eventsHelper.addEventListener(session, 'Network.requestWillBeSent', this._onRequestWillBeSent.bind(this, sessionInfo)),
|
session.addManagedListener('Network.requestWillBeSent', this._onRequestWillBeSent.bind(this, sessionInfo)),
|
||||||
eventsHelper.addEventListener(session, 'Network.requestWillBeSentExtraInfo', this._onRequestWillBeSentExtraInfo.bind(this)),
|
session.addManagedListener('Network.requestWillBeSentExtraInfo', this._onRequestWillBeSentExtraInfo.bind(this)),
|
||||||
eventsHelper.addEventListener(session, 'Network.requestServedFromCache', this._onRequestServedFromCache.bind(this)),
|
session.addManagedListener('Network.requestServedFromCache', this._onRequestServedFromCache.bind(this)),
|
||||||
eventsHelper.addEventListener(session, 'Network.responseReceived', this._onResponseReceived.bind(this, sessionInfo)),
|
session.addManagedListener('Network.responseReceived', this._onResponseReceived.bind(this, sessionInfo)),
|
||||||
eventsHelper.addEventListener(session, 'Network.responseReceivedExtraInfo', this._onResponseReceivedExtraInfo.bind(this)),
|
session.addManagedListener('Network.responseReceivedExtraInfo', this._onResponseReceivedExtraInfo.bind(this)),
|
||||||
eventsHelper.addEventListener(session, 'Network.loadingFinished', this._onLoadingFinished.bind(this, sessionInfo)),
|
session.addManagedListener('Network.loadingFinished', this._onLoadingFinished.bind(this, sessionInfo)),
|
||||||
eventsHelper.addEventListener(session, 'Network.loadingFailed', this._onLoadingFailed.bind(this, sessionInfo)),
|
session.addManagedListener('Network.loadingFailed', this._onLoadingFailed.bind(this, sessionInfo)),
|
||||||
];
|
];
|
||||||
if (this._page) {
|
if (this._page) {
|
||||||
sessionInfo.eventListeners.push(...[
|
sessionInfo.eventListeners.push(...[
|
||||||
eventsHelper.addEventListener(session, 'Network.webSocketCreated', e => this._page!._frameManager.onWebSocketCreated(e.requestId, e.url)),
|
session.addManagedListener('Network.webSocketCreated', e => this._page!._frameManager.onWebSocketCreated(e.requestId, e.url)),
|
||||||
eventsHelper.addEventListener(session, 'Network.webSocketWillSendHandshakeRequest', e => this._page!._frameManager.onWebSocketRequest(e.requestId)),
|
session.addManagedListener('Network.webSocketWillSendHandshakeRequest', e => this._page!._frameManager.onWebSocketRequest(e.requestId)),
|
||||||
eventsHelper.addEventListener(session, 'Network.webSocketHandshakeResponseReceived', e => this._page!._frameManager.onWebSocketResponse(e.requestId, e.response.status, e.response.statusText)),
|
session.addManagedListener('Network.webSocketHandshakeResponseReceived', e => this._page!._frameManager.onWebSocketResponse(e.requestId, e.response.status, e.response.statusText)),
|
||||||
eventsHelper.addEventListener(session, 'Network.webSocketFrameSent', e => e.response.payloadData && this._page!._frameManager.onWebSocketFrameSent(e.requestId, e.response.opcode, e.response.payloadData)),
|
session.addManagedListener('Network.webSocketFrameSent', e => e.response.payloadData && this._page!._frameManager.onWebSocketFrameSent(e.requestId, e.response.opcode, e.response.payloadData)),
|
||||||
eventsHelper.addEventListener(session, 'Network.webSocketFrameReceived', e => e.response.payloadData && this._page!._frameManager.webSocketFrameReceived(e.requestId, e.response.opcode, e.response.payloadData)),
|
session.addManagedListener('Network.webSocketFrameReceived', e => e.response.payloadData && this._page!._frameManager.webSocketFrameReceived(e.requestId, e.response.opcode, e.response.payloadData)),
|
||||||
eventsHelper.addEventListener(session, 'Network.webSocketClosed', e => this._page!._frameManager.webSocketClosed(e.requestId)),
|
session.addManagedListener('Network.webSocketClosed', e => this._page!._frameManager.webSocketClosed(e.requestId)),
|
||||||
eventsHelper.addEventListener(session, 'Network.webSocketFrameError', e => this._page!._frameManager.webSocketError(e.requestId, e.errorMessage)),
|
session.addManagedListener('Network.webSocketFrameError', e => this._page!._frameManager.webSocketError(e.requestId, e.errorMessage)),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
this._sessions.set(session, sessionInfo);
|
this._sessions.set(session, sessionInfo);
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ import type { Progress } from '../progress';
|
||||||
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 { getAccessibilityTree } from './crAccessibility';
|
import { getAccessibilityTree } from './crAccessibility';
|
||||||
import { CRBrowserContext } from './crBrowser';
|
import type { CRBrowserContext } from './crBrowser';
|
||||||
import type { CRSession } from './crConnection';
|
import type { CRSession } from './crConnection';
|
||||||
import { CRCoverage } from './crCoverage';
|
import { CRCoverage } from './crCoverage';
|
||||||
import { DragManager } from './crDragDrop';
|
import { DragManager } from './crDragDrop';
|
||||||
|
|
@ -44,7 +44,6 @@ import { exceptionToError, releaseObject, toConsoleMessageLocation } from './crP
|
||||||
import { platformToFontFamilies } from './defaultFontFamilies';
|
import { platformToFontFamilies } from './defaultFontFamilies';
|
||||||
import type { Protocol } from './protocol';
|
import type { Protocol } from './protocol';
|
||||||
import { VideoRecorder } from './videoRecorder';
|
import { VideoRecorder } from './videoRecorder';
|
||||||
import { BrowserContext } from '../browserContext';
|
|
||||||
import { TargetClosedError } from '../errors';
|
import { TargetClosedError } from '../errors';
|
||||||
import { isSessionClosedError } from '../protocolError';
|
import { isSessionClosedError } from '../protocolError';
|
||||||
|
|
||||||
|
|
@ -131,7 +130,7 @@ export class CRPage implements PageDelegate {
|
||||||
}
|
}
|
||||||
|
|
||||||
private _reportAsNew(error?: Error) {
|
private _reportAsNew(error?: Error) {
|
||||||
this._page.reportAsNew(error, this._isBackgroundPage ? CRBrowserContext.CREvents.BackgroundPage : BrowserContext.Events.Page);
|
this._page.reportAsNew(error, this._isBackgroundPage ? 'backgroundpage' : 'page');
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _forAllFrameSessions(cb: (frame: FrameSession) => Promise<any>) {
|
private async _forAllFrameSessions(cb: (frame: FrameSession) => Promise<any>) {
|
||||||
|
|
@ -444,30 +443,30 @@ class FrameSession {
|
||||||
|
|
||||||
private _addRendererListeners() {
|
private _addRendererListeners() {
|
||||||
this._eventListeners.push(...[
|
this._eventListeners.push(...[
|
||||||
eventsHelper.addEventListener(this._client, 'Log.entryAdded', event => this._onLogEntryAdded(event)),
|
this._client.addManagedListener('Log.entryAdded', event => this._onLogEntryAdded(event)),
|
||||||
eventsHelper.addEventListener(this._client, 'Page.fileChooserOpened', event => this._onFileChooserOpened(event)),
|
this._client.addManagedListener('Page.fileChooserOpened', event => this._onFileChooserOpened(event)),
|
||||||
eventsHelper.addEventListener(this._client, 'Page.frameAttached', event => this._onFrameAttached(event.frameId, event.parentFrameId)),
|
this._client.addManagedListener('Page.frameAttached', event => this._onFrameAttached(event.frameId, event.parentFrameId)),
|
||||||
eventsHelper.addEventListener(this._client, 'Page.frameDetached', event => this._onFrameDetached(event.frameId, event.reason)),
|
this._client.addManagedListener('Page.frameDetached', event => this._onFrameDetached(event.frameId, event.reason)),
|
||||||
eventsHelper.addEventListener(this._client, 'Page.frameNavigated', event => this._onFrameNavigated(event.frame, false)),
|
this._client.addManagedListener('Page.frameNavigated', event => this._onFrameNavigated(event.frame, false)),
|
||||||
eventsHelper.addEventListener(this._client, 'Page.frameRequestedNavigation', event => this._onFrameRequestedNavigation(event)),
|
this._client.addManagedListener('Page.frameRequestedNavigation', event => this._onFrameRequestedNavigation(event)),
|
||||||
eventsHelper.addEventListener(this._client, 'Page.javascriptDialogOpening', event => this._onDialog(event)),
|
this._client.addManagedListener('Page.javascriptDialogOpening', event => this._onDialog(event)),
|
||||||
eventsHelper.addEventListener(this._client, 'Page.navigatedWithinDocument', event => this._onFrameNavigatedWithinDocument(event.frameId, event.url)),
|
this._client.addManagedListener('Page.navigatedWithinDocument', event => this._onFrameNavigatedWithinDocument(event.frameId, event.url)),
|
||||||
eventsHelper.addEventListener(this._client, 'Runtime.bindingCalled', event => this._onBindingCalled(event)),
|
this._client.addManagedListener('Runtime.bindingCalled', event => this._onBindingCalled(event)),
|
||||||
eventsHelper.addEventListener(this._client, 'Runtime.consoleAPICalled', event => this._onConsoleAPI(event)),
|
this._client.addManagedListener('Runtime.consoleAPICalled', event => this._onConsoleAPI(event)),
|
||||||
eventsHelper.addEventListener(this._client, 'Runtime.exceptionThrown', exception => this._handleException(exception.exceptionDetails)),
|
this._client.addManagedListener('Runtime.exceptionThrown', exception => this._handleException(exception.exceptionDetails)),
|
||||||
eventsHelper.addEventListener(this._client, 'Runtime.executionContextCreated', event => this._onExecutionContextCreated(event.context)),
|
this._client.addManagedListener('Runtime.executionContextCreated', event => this._onExecutionContextCreated(event.context)),
|
||||||
eventsHelper.addEventListener(this._client, 'Runtime.executionContextDestroyed', event => this._onExecutionContextDestroyed(event.executionContextId)),
|
this._client.addManagedListener('Runtime.executionContextDestroyed', event => this._onExecutionContextDestroyed(event.executionContextId)),
|
||||||
eventsHelper.addEventListener(this._client, 'Runtime.executionContextsCleared', event => this._onExecutionContextsCleared()),
|
this._client.addManagedListener('Runtime.executionContextsCleared', event => this._onExecutionContextsCleared()),
|
||||||
eventsHelper.addEventListener(this._client, 'Target.attachedToTarget', event => this._onAttachedToTarget(event)),
|
this._client.addManagedListener('Target.attachedToTarget', event => this._onAttachedToTarget(event)),
|
||||||
eventsHelper.addEventListener(this._client, 'Target.detachedFromTarget', event => this._onDetachedFromTarget(event)),
|
this._client.addManagedListener('Target.detachedFromTarget', event => this._onDetachedFromTarget(event)),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _addBrowserListeners() {
|
private _addBrowserListeners() {
|
||||||
this._eventListeners.push(...[
|
this._eventListeners.push(...[
|
||||||
eventsHelper.addEventListener(this._client, 'Inspector.targetCrashed', event => this._onTargetCrashed()),
|
this._client.addManagedListener('Inspector.targetCrashed', event => this._onTargetCrashed()),
|
||||||
eventsHelper.addEventListener(this._client, 'Page.screencastFrame', event => this._onScreencastFrame(event)),
|
this._client.addManagedListener('Page.screencastFrame', event => this._onScreencastFrame(event)),
|
||||||
eventsHelper.addEventListener(this._client, 'Page.windowOpen', event => this._onWindowOpen(event)),
|
this._client.addManagedListener('Page.windowOpen', event => this._onWindowOpen(event)),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -531,11 +530,11 @@ class FrameSession {
|
||||||
// hence we are going to get more lifecycle updates after the actual navigation has
|
// hence we are going to get more lifecycle updates after the actual navigation has
|
||||||
// started (even if the target url is about:blank).
|
// started (even if the target url is about:blank).
|
||||||
lifecycleEventsEnabled.catch(e => {}).then(() => {
|
lifecycleEventsEnabled.catch(e => {}).then(() => {
|
||||||
this._eventListeners.push(eventsHelper.addEventListener(this._client, 'Page.lifecycleEvent', event => this._onLifecycleEvent(event)));
|
this._eventListeners.push(this._client.addManagedListener('Page.lifecycleEvent', event => this._onLifecycleEvent(event)));
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
this._firstNonInitialNavigationCommittedFulfill();
|
this._firstNonInitialNavigationCommittedFulfill();
|
||||||
this._eventListeners.push(eventsHelper.addEventListener(this._client, 'Page.lifecycleEvent', event => this._onLifecycleEvent(event)));
|
this._eventListeners.push(this._client.addManagedListener('Page.lifecycleEvent', event => this._onLifecycleEvent(event)));
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
this._client.send('Log.enable', {}),
|
this._client.send('Log.enable', {}),
|
||||||
|
|
@ -770,7 +769,7 @@ class FrameSession {
|
||||||
const args = event.args.map(o => worker._existingExecutionContext!.createHandle(o));
|
const args = event.args.map(o => worker._existingExecutionContext!.createHandle(o));
|
||||||
this._page._addConsoleMessage(event.type, args, toConsoleMessageLocation(event.stackTrace));
|
this._page._addConsoleMessage(event.type, args, toConsoleMessageLocation(event.stackTrace));
|
||||||
});
|
});
|
||||||
session.on('Runtime.exceptionThrown', exception => this._page.emitOnContextOnceInitialized(BrowserContext.Events.PageError, exceptionToError(exception.exceptionDetails), this._page));
|
session.on('Runtime.exceptionThrown', exception => this._page.emitOnContextOnceInitialized('pageerror', exceptionToError(exception.exceptionDetails), this._page));
|
||||||
}
|
}
|
||||||
|
|
||||||
_onDetachedFromTarget(event: Protocol.Target.detachedFromTargetPayload) {
|
_onDetachedFromTarget(event: Protocol.Target.detachedFromTargetPayload) {
|
||||||
|
|
@ -865,7 +864,7 @@ class FrameSession {
|
||||||
_onDialog(event: Protocol.Page.javascriptDialogOpeningPayload) {
|
_onDialog(event: Protocol.Page.javascriptDialogOpeningPayload) {
|
||||||
if (!this._page._frameManager.frame(this._targetId))
|
if (!this._page._frameManager.frame(this._targetId))
|
||||||
return; // Our frame/subtree may be gone already.
|
return; // Our frame/subtree may be gone already.
|
||||||
this._page.emitOnContext(BrowserContext.Events.Dialog, new dialog.Dialog(
|
this._page.emitOnContext('dialog', new dialog.Dialog(
|
||||||
this._page,
|
this._page,
|
||||||
event.type,
|
event.type,
|
||||||
event.message,
|
event.message,
|
||||||
|
|
@ -876,7 +875,7 @@ class FrameSession {
|
||||||
}
|
}
|
||||||
|
|
||||||
_handleException(exceptionDetails: Protocol.Runtime.ExceptionDetails) {
|
_handleException(exceptionDetails: Protocol.Runtime.ExceptionDetails) {
|
||||||
this._page.emitOnContextOnceInitialized(BrowserContext.Events.PageError, exceptionToError(exceptionDetails), this._page);
|
this._page.emitOnContextOnceInitialized('pageerror', exceptionToError(exceptionDetails), this._page);
|
||||||
}
|
}
|
||||||
|
|
||||||
async _onTargetCrashed() {
|
async _onTargetCrashed() {
|
||||||
|
|
@ -929,7 +928,7 @@ class FrameSession {
|
||||||
this._client.send('Page.screencastFrameAck', { sessionId: payload.sessionId }).catch(() => {});
|
this._client.send('Page.screencastFrameAck', { sessionId: payload.sessionId }).catch(() => {});
|
||||||
});
|
});
|
||||||
const buffer = Buffer.from(payload.data, 'base64');
|
const buffer = Buffer.from(payload.data, 'base64');
|
||||||
this._page.emit(Page.Events.ScreencastFrame, {
|
this._page.emit('screencastframe', {
|
||||||
buffer,
|
buffer,
|
||||||
timestamp: payload.metadata.timestamp,
|
timestamp: payload.metadata.timestamp,
|
||||||
width: payload.metadata.deviceWidth,
|
width: payload.metadata.deviceWidth,
|
||||||
|
|
@ -947,7 +946,7 @@ class FrameSession {
|
||||||
async _startVideoRecording(options: types.PageScreencastOptions) {
|
async _startVideoRecording(options: types.PageScreencastOptions) {
|
||||||
const screencastId = this._screencastId;
|
const screencastId = this._screencastId;
|
||||||
assert(screencastId);
|
assert(screencastId);
|
||||||
this._page.once(Page.Events.Close, () => this._stopVideoRecording().catch(() => {}));
|
this._page.once('close', () => this._stopVideoRecording().catch(() => {}));
|
||||||
const gotFirstFrame = new Promise(f => this._client.once('Page.screencastFrame', f));
|
const gotFirstFrame = new Promise(f => this._client.once('Page.screencastFrame', f));
|
||||||
await this._startScreencast(this._videoRecorder, {
|
await this._startScreencast(this._videoRecorder, {
|
||||||
format: 'jpeg',
|
format: 'jpeg',
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,6 @@ import type { CRSession } from './crConnection';
|
||||||
import { CRExecutionContext } from './crExecutionContext';
|
import { CRExecutionContext } from './crExecutionContext';
|
||||||
import { CRNetworkManager } from './crNetworkManager';
|
import { CRNetworkManager } from './crNetworkManager';
|
||||||
import * as network from '../network';
|
import * as network from '../network';
|
||||||
import { BrowserContext } from '../browserContext';
|
|
||||||
|
|
||||||
export class CRServiceWorker extends Worker {
|
export class CRServiceWorker extends Worker {
|
||||||
readonly _browserContext: CRBrowserContext;
|
readonly _browserContext: CRBrowserContext;
|
||||||
|
|
@ -87,19 +86,19 @@ export class CRServiceWorker extends Worker {
|
||||||
}
|
}
|
||||||
|
|
||||||
reportRequestFinished(request: network.Request, response: network.Response | null) {
|
reportRequestFinished(request: network.Request, response: network.Response | null) {
|
||||||
this._browserContext.emit(BrowserContext.Events.RequestFinished, { request, response });
|
this._browserContext.emit('requestfinished', { request, response });
|
||||||
}
|
}
|
||||||
|
|
||||||
requestFailed(request: network.Request, _canceled: boolean) {
|
requestFailed(request: network.Request, _canceled: boolean) {
|
||||||
this._browserContext.emit(BrowserContext.Events.RequestFailed, request);
|
this._browserContext.emit('requestfailed', request);
|
||||||
}
|
}
|
||||||
|
|
||||||
requestReceivedResponse(response: network.Response) {
|
requestReceivedResponse(response: network.Response) {
|
||||||
this._browserContext.emit(BrowserContext.Events.Response, response);
|
this._browserContext.emit('response', response);
|
||||||
}
|
}
|
||||||
|
|
||||||
requestStarted(request: network.Request, route?: network.RouteDelegate) {
|
requestStarted(request: network.Request, route?: network.RouteDelegate) {
|
||||||
this._browserContext.emit(BrowserContext.Events.Request, request);
|
this._browserContext.emit('request', request);
|
||||||
if (route) {
|
if (route) {
|
||||||
const r = new network.Route(request, route);
|
const r = new network.Route(request, route);
|
||||||
if (this._browserContext._requestInterceptor?.(r, request))
|
if (this._browserContext._requestInterceptor?.(r, request))
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@
|
||||||
|
|
||||||
import type { ChildProcess } from 'child_process';
|
import type { ChildProcess } from 'child_process';
|
||||||
import { assert, monotonicTime } from '../../utils';
|
import { assert, monotonicTime } from '../../utils';
|
||||||
import { Page } from '../page';
|
import type { Page } from '../page';
|
||||||
import { launchProcess } from '../../utils/processLauncher';
|
import { launchProcess } from '../../utils/processLauncher';
|
||||||
import type { Progress } from '../progress';
|
import type { Progress } from '../progress';
|
||||||
import { ProgressController } from '../progress';
|
import { ProgressController } from '../progress';
|
||||||
|
|
@ -53,7 +53,7 @@ export class VideoRecorder {
|
||||||
private constructor(page: Page, ffmpegPath: string, progress: Progress) {
|
private constructor(page: Page, ffmpegPath: string, progress: Progress) {
|
||||||
this._progress = progress;
|
this._progress = progress;
|
||||||
this._ffmpegPath = ffmpegPath;
|
this._ffmpegPath = ffmpegPath;
|
||||||
page.on(Page.Events.ScreencastFrame, frame => this.writeFrame(frame.buffer, frame.timestamp));
|
page.on('screencastframe', frame => this.writeFrame(frame.buffer, frame.timestamp!));
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _launch(options: types.PageScreencastOptions) {
|
private async _launch(options: types.PageScreencastOptions) {
|
||||||
|
|
|
||||||
|
|
@ -28,15 +28,13 @@ import type { Language } from '../utils/isomorphic/locatorGenerators';
|
||||||
|
|
||||||
const internalMetadata = serverSideCallMetadata();
|
const internalMetadata = serverSideCallMetadata();
|
||||||
|
|
||||||
export class DebugController extends SdkObject {
|
export class DebugController extends SdkObject<{
|
||||||
static Events = {
|
stateChanged: [{ pageCount: number }]
|
||||||
StateChanged: 'stateChanged',
|
inspectRequested: [{ selector: string, locator: string }]
|
||||||
InspectRequested: 'inspectRequested',
|
sourceChanged: [{ text: string, header: string | undefined, footer: string | undefined, actions: string[] | undefined }]
|
||||||
SourceChanged: 'sourceChanged',
|
paused: [{ paused: boolean }]
|
||||||
Paused: 'paused',
|
setModeRequested: [{ mode: Mode }]
|
||||||
SetModeRequested: 'setModeRequested',
|
}> {
|
||||||
};
|
|
||||||
|
|
||||||
private _autoCloseTimer: NodeJS.Timeout | undefined;
|
private _autoCloseTimer: NodeJS.Timeout | undefined;
|
||||||
// TODO: remove in 1.27
|
// TODO: remove in 1.27
|
||||||
private _autoCloseAllowed = false;
|
private _autoCloseAllowed = false;
|
||||||
|
|
@ -193,7 +191,7 @@ export class DebugController extends SdkObject {
|
||||||
pageCount += context.pages().length;
|
pageCount += context.pages().length;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.emit(DebugController.Events.StateChanged, { pageCount });
|
this.emit('stateChanged', { pageCount });
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _allRecorders(): Promise<Recorder[]> {
|
private async _allRecorders(): Promise<Recorder[]> {
|
||||||
|
|
@ -226,20 +224,20 @@ class InspectingRecorderApp extends EmptyRecorderApp {
|
||||||
|
|
||||||
override async setSelector(selector: string): Promise<void> {
|
override async setSelector(selector: string): Promise<void> {
|
||||||
const locator: string = asLocator(this._debugController._sdkLanguage, selector);
|
const locator: string = asLocator(this._debugController._sdkLanguage, selector);
|
||||||
this._debugController.emit(DebugController.Events.InspectRequested, { selector, locator });
|
this._debugController.emit('inspectRequested', { selector, locator });
|
||||||
}
|
}
|
||||||
|
|
||||||
override async setSources(sources: Source[]): Promise<void> {
|
override async setSources(sources: Source[]): Promise<void> {
|
||||||
const source = sources.find(s => s.id === this._debugController._codegenId);
|
const source = sources.find(s => s.id === this._debugController._codegenId);
|
||||||
const { text, header, footer, actions } = source || { text: '' };
|
const { text, header, footer, actions } = source || { text: '' };
|
||||||
this._debugController.emit(DebugController.Events.SourceChanged, { text, header, footer, actions });
|
this._debugController.emit('sourceChanged', { text, header, footer, actions });
|
||||||
}
|
}
|
||||||
|
|
||||||
override async setPaused(paused: boolean) {
|
override async setPaused(paused: boolean) {
|
||||||
this._debugController.emit(DebugController.Events.Paused, { paused });
|
this._debugController.emit('paused', { paused });
|
||||||
}
|
}
|
||||||
|
|
||||||
override async setMode(mode: Mode) {
|
override async setMode(mode: Mode) {
|
||||||
this._debugController.emit(DebugController.Events.SetModeRequested, { mode });
|
this._debugController.emit('setModeRequested', { mode });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,21 +16,20 @@
|
||||||
|
|
||||||
import { EventEmitter } from 'events';
|
import { EventEmitter } from 'events';
|
||||||
import { debugMode, isUnderTest, monotonicTime } from '../utils';
|
import { debugMode, isUnderTest, monotonicTime } from '../utils';
|
||||||
import { BrowserContext } from './browserContext';
|
import type { BrowserContext } from './browserContext';
|
||||||
import type { CallMetadata, InstrumentationListener, SdkObject } from './instrumentation';
|
import type { CallMetadata, InstrumentationListener, SdkObject } from './instrumentation';
|
||||||
import { commandsWithTracingSnapshots, pausesBeforeInputActions, slowMoActions } from '../protocol/debug';
|
import { commandsWithTracingSnapshots, pausesBeforeInputActions, slowMoActions } from '../protocol/debug';
|
||||||
|
|
||||||
const symbol = Symbol('Debugger');
|
const symbol = Symbol('Debugger');
|
||||||
|
|
||||||
export class Debugger extends EventEmitter implements InstrumentationListener {
|
export class Debugger extends EventEmitter<{
|
||||||
|
pausedstatechanged: []
|
||||||
|
}> implements InstrumentationListener {
|
||||||
private _pauseOnNextStatement = false;
|
private _pauseOnNextStatement = false;
|
||||||
private _pausedCallsMetadata = new Map<CallMetadata, { resolve: () => void, sdkObject: SdkObject }>();
|
private _pausedCallsMetadata = new Map<CallMetadata, { resolve: () => void, sdkObject: SdkObject }>();
|
||||||
private _enabled: boolean;
|
private _enabled: boolean;
|
||||||
private _context: BrowserContext;
|
private _context: BrowserContext;
|
||||||
|
|
||||||
static Events = {
|
|
||||||
PausedStateChanged: 'pausedstatechanged'
|
|
||||||
};
|
|
||||||
private _muted = false;
|
private _muted = false;
|
||||||
private _slowMo: number | undefined;
|
private _slowMo: number | undefined;
|
||||||
|
|
||||||
|
|
@ -42,7 +41,7 @@ export class Debugger extends EventEmitter implements InstrumentationListener {
|
||||||
if (this._enabled)
|
if (this._enabled)
|
||||||
this.pauseOnNextStatement();
|
this.pauseOnNextStatement();
|
||||||
context.instrumentation.addListener(this, context);
|
context.instrumentation.addListener(this, context);
|
||||||
this._context.once(BrowserContext.Events.Close, () => {
|
this._context.once('close', () => {
|
||||||
this._context.instrumentation.removeListener(this);
|
this._context.instrumentation.removeListener(this);
|
||||||
});
|
});
|
||||||
this._slowMo = this._context._browser.options.slowMo;
|
this._slowMo = this._context._browser.options.slowMo;
|
||||||
|
|
@ -83,7 +82,7 @@ export class Debugger extends EventEmitter implements InstrumentationListener {
|
||||||
const result = new Promise<void>(resolve => {
|
const result = new Promise<void>(resolve => {
|
||||||
this._pausedCallsMetadata.set(metadata, { resolve, sdkObject });
|
this._pausedCallsMetadata.set(metadata, { resolve, sdkObject });
|
||||||
});
|
});
|
||||||
this.emit(Debugger.Events.PausedStateChanged);
|
this.emit('pausedstatechanged');
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -98,7 +97,7 @@ export class Debugger extends EventEmitter implements InstrumentationListener {
|
||||||
resolve();
|
resolve();
|
||||||
}
|
}
|
||||||
this._pausedCallsMetadata.clear();
|
this._pausedCallsMetadata.clear();
|
||||||
this.emit(Debugger.Events.PausedStateChanged);
|
this.emit('pausedstatechanged');
|
||||||
}
|
}
|
||||||
|
|
||||||
pauseOnNextStatement() {
|
pauseOnNextStatement() {
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@
|
||||||
import type { RootDispatcher } from './dispatcher';
|
import type { RootDispatcher } from './dispatcher';
|
||||||
import { Dispatcher, existingDispatcher } from './dispatcher';
|
import { Dispatcher, existingDispatcher } from './dispatcher';
|
||||||
import type { Android, SocketBackend } from '../android/android';
|
import type { Android, SocketBackend } from '../android/android';
|
||||||
import { AndroidDevice } from '../android/android';
|
import type { AndroidDevice } from '../android/android';
|
||||||
import type * as channels from '@protocol/channels';
|
import type * as channels from '@protocol/channels';
|
||||||
import { BrowserContextDispatcher } from './browserContextDispatcher';
|
import { BrowserContextDispatcher } from './browserContextDispatcher';
|
||||||
import type { CallMetadata } from '../instrumentation';
|
import type { CallMetadata } from '../instrumentation';
|
||||||
|
|
@ -56,9 +56,9 @@ export class AndroidDeviceDispatcher extends Dispatcher<AndroidDevice, channels.
|
||||||
});
|
});
|
||||||
for (const webView of device.webViews())
|
for (const webView of device.webViews())
|
||||||
this._dispatchEvent('webViewAdded', { webView });
|
this._dispatchEvent('webViewAdded', { webView });
|
||||||
this.addObjectListener(AndroidDevice.Events.WebViewAdded, webView => this._dispatchEvent('webViewAdded', { webView }));
|
this.addObjectListener('webViewAdded', webView => this._dispatchEvent('webViewAdded', { webView }));
|
||||||
this.addObjectListener(AndroidDevice.Events.WebViewRemoved, socketName => this._dispatchEvent('webViewRemoved', { socketName }));
|
this.addObjectListener('webViewRemoved', socketName => this._dispatchEvent('webViewRemoved', { socketName }));
|
||||||
this.addObjectListener(AndroidDevice.Events.Close, socketName => this._dispatchEvent('close'));
|
this.addObjectListener('close', () => this._dispatchEvent('close'));
|
||||||
}
|
}
|
||||||
|
|
||||||
async wait(params: channels.AndroidDeviceWaitParams) {
|
async wait(params: channels.AndroidDeviceWaitParams) {
|
||||||
|
|
|
||||||
|
|
@ -14,14 +14,14 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { BrowserContext } from '../browserContext';
|
import type { BrowserContext } from '../browserContext';
|
||||||
import { Dispatcher, existingDispatcher } from './dispatcher';
|
import { Dispatcher, existingDispatcher } from './dispatcher';
|
||||||
import type { DispatcherScope } from './dispatcher';
|
import type { DispatcherScope } from './dispatcher';
|
||||||
import { PageDispatcher, BindingCallDispatcher, WorkerDispatcher } from './pageDispatcher';
|
import { PageDispatcher, BindingCallDispatcher, WorkerDispatcher } from './pageDispatcher';
|
||||||
import type { FrameDispatcher } from './frameDispatcher';
|
import type { FrameDispatcher } from './frameDispatcher';
|
||||||
import type * as channels from '@protocol/channels';
|
import type * as channels from '@protocol/channels';
|
||||||
import { RouteDispatcher, RequestDispatcher, ResponseDispatcher, APIRequestContextDispatcher } from './networkDispatchers';
|
import { RouteDispatcher, RequestDispatcher, ResponseDispatcher, APIRequestContextDispatcher } from './networkDispatchers';
|
||||||
import { CRBrowserContext } from '../chromium/crBrowser';
|
import type { CRBrowserContext } from '../chromium/crBrowser';
|
||||||
import { CDPSessionDispatcher } from './cdpSessionDispatcher';
|
import { CDPSessionDispatcher } from './cdpSessionDispatcher';
|
||||||
import { Recorder } from '../recorder';
|
import { Recorder } from '../recorder';
|
||||||
import type { CallMetadata } from '../instrumentation';
|
import type { CallMetadata } from '../instrumentation';
|
||||||
|
|
@ -71,7 +71,7 @@ export class BrowserContextDispatcher extends Dispatcher<BrowserContext, channel
|
||||||
const artifactDispatcher = ArtifactDispatcher.from(parentScope, artifact);
|
const artifactDispatcher = ArtifactDispatcher.from(parentScope, artifact);
|
||||||
this._dispatchEvent('video', { artifact: artifactDispatcher });
|
this._dispatchEvent('video', { artifact: artifactDispatcher });
|
||||||
};
|
};
|
||||||
this.addObjectListener(BrowserContext.Events.VideoStarted, onVideo);
|
this.addObjectListener('videostarted', onVideo);
|
||||||
for (const video of context._browser._idToVideo.values()) {
|
for (const video of context._browser._idToVideo.values()) {
|
||||||
if (video.context === context)
|
if (video.context === context)
|
||||||
onVideo(video.artifact);
|
onVideo(video.artifact);
|
||||||
|
|
@ -79,17 +79,17 @@ export class BrowserContextDispatcher extends Dispatcher<BrowserContext, channel
|
||||||
|
|
||||||
for (const page of context.pages())
|
for (const page of context.pages())
|
||||||
this._dispatchEvent('page', { page: PageDispatcher.from(this, page) });
|
this._dispatchEvent('page', { page: PageDispatcher.from(this, page) });
|
||||||
this.addObjectListener(BrowserContext.Events.Page, page => {
|
this.addObjectListener('page', page => {
|
||||||
this._dispatchEvent('page', { page: PageDispatcher.from(this, page) });
|
this._dispatchEvent('page', { page: PageDispatcher.from(this, page) });
|
||||||
});
|
});
|
||||||
this.addObjectListener(BrowserContext.Events.Close, () => {
|
this.addObjectListener('close', () => {
|
||||||
this._dispatchEvent('close');
|
this._dispatchEvent('close');
|
||||||
this._dispose();
|
this._dispose();
|
||||||
});
|
});
|
||||||
this.addObjectListener(BrowserContext.Events.PageError, (error: Error, page: Page) => {
|
this.addObjectListener('pageerror', (error: Error, page: Page) => {
|
||||||
this._dispatchEvent('pageError', { error: serializeError(error), page: PageDispatcher.from(this, page) });
|
this._dispatchEvent('pageError', { error: serializeError(error), page: PageDispatcher.from(this, page) });
|
||||||
});
|
});
|
||||||
this.addObjectListener(BrowserContext.Events.Console, (message: ConsoleMessage) => {
|
this.addObjectListener('console', (message: ConsoleMessage) => {
|
||||||
const page = message.page()!;
|
const page = message.page()!;
|
||||||
if (this._shouldDispatchEvent(page, 'console')) {
|
if (this._shouldDispatchEvent(page, 'console')) {
|
||||||
const pageDispatcher = PageDispatcher.from(this, page);
|
const pageDispatcher = PageDispatcher.from(this, page);
|
||||||
|
|
@ -102,7 +102,7 @@ export class BrowserContextDispatcher extends Dispatcher<BrowserContext, channel
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
this.addObjectListener(BrowserContext.Events.Dialog, (dialog: Dialog) => {
|
this.addObjectListener('dialog', (dialog: Dialog) => {
|
||||||
if (this._shouldDispatchEvent(dialog.page(), 'dialog'))
|
if (this._shouldDispatchEvent(dialog.page(), 'dialog'))
|
||||||
this._dispatchEvent('dialog', { dialog: new DialogDispatcher(this, dialog) });
|
this._dispatchEvent('dialog', { dialog: new DialogDispatcher(this, dialog) });
|
||||||
else
|
else
|
||||||
|
|
@ -112,12 +112,12 @@ export class BrowserContextDispatcher extends Dispatcher<BrowserContext, channel
|
||||||
if (context._browser.options.name === 'chromium') {
|
if (context._browser.options.name === 'chromium') {
|
||||||
for (const page of (context as CRBrowserContext).backgroundPages())
|
for (const page of (context as CRBrowserContext).backgroundPages())
|
||||||
this._dispatchEvent('backgroundPage', { page: PageDispatcher.from(this, page) });
|
this._dispatchEvent('backgroundPage', { page: PageDispatcher.from(this, page) });
|
||||||
this.addObjectListener(CRBrowserContext.CREvents.BackgroundPage, page => this._dispatchEvent('backgroundPage', { page: PageDispatcher.from(this, page) }));
|
this.addObjectListener('backgroundpage', page => this._dispatchEvent('backgroundPage', { page: PageDispatcher.from(this, page) }));
|
||||||
for (const serviceWorker of (context as CRBrowserContext).serviceWorkers())
|
for (const serviceWorker of (context as CRBrowserContext).serviceWorkers())
|
||||||
this._dispatchEvent('serviceWorker', { worker: new WorkerDispatcher(this, serviceWorker) });
|
this._dispatchEvent('serviceWorker', { worker: new WorkerDispatcher(this, serviceWorker) });
|
||||||
this.addObjectListener(CRBrowserContext.CREvents.ServiceWorker, serviceWorker => this._dispatchEvent('serviceWorker', { worker: new WorkerDispatcher(this, serviceWorker) }));
|
this.addObjectListener('serviceworker', serviceWorker => this._dispatchEvent('serviceWorker', { worker: new WorkerDispatcher(this, serviceWorker) }));
|
||||||
}
|
}
|
||||||
this.addObjectListener(BrowserContext.Events.Request, (request: Request) => {
|
this.addObjectListener('request', (request: Request) => {
|
||||||
// Create dispatcher, if:
|
// Create dispatcher, if:
|
||||||
// - There are listeners to the requests.
|
// - There are listeners to the requests.
|
||||||
// - We are redirected from a reported request so that redirectedTo was updated on client.
|
// - We are redirected from a reported request so that redirectedTo was updated on client.
|
||||||
|
|
@ -132,7 +132,7 @@ export class BrowserContextDispatcher extends Dispatcher<BrowserContext, channel
|
||||||
page: PageDispatcher.fromNullable(this, request.frame()?._page.initializedOrUndefined())
|
page: PageDispatcher.fromNullable(this, request.frame()?._page.initializedOrUndefined())
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
this.addObjectListener(BrowserContext.Events.Response, (response: Response) => {
|
this.addObjectListener('response', (response: Response) => {
|
||||||
const requestDispatcher = existingDispatcher<RequestDispatcher>(response.request());
|
const requestDispatcher = existingDispatcher<RequestDispatcher>(response.request());
|
||||||
if (!requestDispatcher && !this._shouldDispatchNetworkEvent(response.request(), 'response'))
|
if (!requestDispatcher && !this._shouldDispatchNetworkEvent(response.request(), 'response'))
|
||||||
return;
|
return;
|
||||||
|
|
@ -141,7 +141,7 @@ export class BrowserContextDispatcher extends Dispatcher<BrowserContext, channel
|
||||||
page: PageDispatcher.fromNullable(this, response.frame()?._page.initializedOrUndefined())
|
page: PageDispatcher.fromNullable(this, response.frame()?._page.initializedOrUndefined())
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
this.addObjectListener(BrowserContext.Events.RequestFailed, (request: Request) => {
|
this.addObjectListener('requestfailed', (request: Request) => {
|
||||||
const requestDispatcher = existingDispatcher<RequestDispatcher>(request);
|
const requestDispatcher = existingDispatcher<RequestDispatcher>(request);
|
||||||
if (!requestDispatcher && !this._shouldDispatchNetworkEvent(request, 'requestFailed'))
|
if (!requestDispatcher && !this._shouldDispatchNetworkEvent(request, 'requestFailed'))
|
||||||
return;
|
return;
|
||||||
|
|
@ -152,7 +152,7 @@ export class BrowserContextDispatcher extends Dispatcher<BrowserContext, channel
|
||||||
page: PageDispatcher.fromNullable(this, request.frame()?._page.initializedOrUndefined())
|
page: PageDispatcher.fromNullable(this, request.frame()?._page.initializedOrUndefined())
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
this.addObjectListener(BrowserContext.Events.RequestFinished, ({ request, response }: { request: Request, response: Response | null }) => {
|
this.addObjectListener('requestfinished', ({ request, response }: { request: Request, response: Response | null }) => {
|
||||||
const requestDispatcher = existingDispatcher<RequestDispatcher>(request);
|
const requestDispatcher = existingDispatcher<RequestDispatcher>(request);
|
||||||
if (!requestDispatcher && !this._shouldDispatchNetworkEvent(request, 'requestFinished'))
|
if (!requestDispatcher && !this._shouldDispatchNetworkEvent(request, 'requestFinished'))
|
||||||
return;
|
return;
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Browser } from '../browser';
|
import type { Browser } from '../browser';
|
||||||
import type * as channels from '@protocol/channels';
|
import type * as channels from '@protocol/channels';
|
||||||
import { BrowserContextDispatcher } from './browserContextDispatcher';
|
import { BrowserContextDispatcher } from './browserContextDispatcher';
|
||||||
import { CDPSessionDispatcher } from './cdpSessionDispatcher';
|
import { CDPSessionDispatcher } from './cdpSessionDispatcher';
|
||||||
|
|
@ -24,7 +24,7 @@ import { Dispatcher } from './dispatcher';
|
||||||
import type { CRBrowser } from '../chromium/crBrowser';
|
import type { CRBrowser } from '../chromium/crBrowser';
|
||||||
import type { PageDispatcher } from './pageDispatcher';
|
import type { PageDispatcher } from './pageDispatcher';
|
||||||
import type { CallMetadata } from '../instrumentation';
|
import type { CallMetadata } from '../instrumentation';
|
||||||
import { BrowserContext } from '../browserContext';
|
import type { BrowserContext } from '../browserContext';
|
||||||
import { Selectors } from '../selectors';
|
import { Selectors } from '../selectors';
|
||||||
import type { BrowserTypeDispatcher } from './browserTypeDispatcher';
|
import type { BrowserTypeDispatcher } from './browserTypeDispatcher';
|
||||||
import { ArtifactDispatcher } from './artifactDispatcher';
|
import { ArtifactDispatcher } from './artifactDispatcher';
|
||||||
|
|
@ -35,8 +35,8 @@ export class BrowserDispatcher extends Dispatcher<Browser, channels.BrowserChann
|
||||||
constructor(scope: BrowserTypeDispatcher, browser: Browser) {
|
constructor(scope: BrowserTypeDispatcher, browser: Browser) {
|
||||||
super(scope, browser, 'Browser', { version: browser.version(), name: browser.options.name });
|
super(scope, browser, 'Browser', { version: browser.version(), name: browser.options.name });
|
||||||
browser.setNeedsBeforeCloseEvent(true);
|
browser.setNeedsBeforeCloseEvent(true);
|
||||||
this.addObjectListener(Browser.Events.BeforeClose, () => this._dispatchEvent('beforeClose'));
|
this.addObjectListener('beforeClose', () => this._dispatchEvent('beforeClose'));
|
||||||
this.addObjectListener(Browser.Events.Disconnected, () => this._didClose());
|
this.addObjectListener('disconnected', () => this._didClose());
|
||||||
}
|
}
|
||||||
|
|
||||||
override _onDispose() {
|
override _onDispose() {
|
||||||
|
|
@ -109,7 +109,7 @@ export class ConnectedBrowserDispatcher extends Dispatcher<Browser, channels.Bro
|
||||||
|
|
||||||
constructor(scope: RootDispatcher, browser: Browser) {
|
constructor(scope: RootDispatcher, browser: Browser) {
|
||||||
super(scope, browser, 'Browser', { version: browser.version(), name: browser.options.name });
|
super(scope, browser, 'Browser', { version: browser.version(), name: browser.options.name });
|
||||||
this.addObjectListener(Browser.Events.BeforeClose, () => this.emit('beforeClose'));
|
this.addObjectListener('beforeClose', () => this.emit('beforeClose'));
|
||||||
// When we have a remotely-connected browser, each client gets a fresh Selector instance,
|
// When we have a remotely-connected browser, each client gets a fresh Selector instance,
|
||||||
// so that two clients do not interfere between each other.
|
// so that two clients do not interfere between each other.
|
||||||
this.selectors = new Selectors();
|
this.selectors = new Selectors();
|
||||||
|
|
@ -121,7 +121,7 @@ export class ConnectedBrowserDispatcher extends Dispatcher<Browser, channels.Bro
|
||||||
const context = await this._object.newContext(metadata, params);
|
const context = await this._object.newContext(metadata, params);
|
||||||
this._contexts.add(context);
|
this._contexts.add(context);
|
||||||
context.setSelectors(this.selectors);
|
context.setSelectors(this.selectors);
|
||||||
context.on(BrowserContext.Events.Close, () => this._contexts.delete(context));
|
context.on('close', () => this._contexts.delete(context));
|
||||||
return { context: new BrowserContextDispatcher(this, context) };
|
return { context: new BrowserContextDispatcher(this, context) };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { CDPSession } from '../chromium/crConnection';
|
import type { CDPSession } from '../chromium/crConnection';
|
||||||
import type * as channels from '@protocol/channels';
|
import type * as channels from '@protocol/channels';
|
||||||
import { Dispatcher } from './dispatcher';
|
import { Dispatcher } from './dispatcher';
|
||||||
import type { BrowserDispatcher } from './browserDispatcher';
|
import type { BrowserDispatcher } from './browserDispatcher';
|
||||||
|
|
@ -26,8 +26,8 @@ export class CDPSessionDispatcher extends Dispatcher<CDPSession, channels.CDPSes
|
||||||
|
|
||||||
constructor(scope: BrowserDispatcher | BrowserContextDispatcher, cdpSession: CDPSession) {
|
constructor(scope: BrowserDispatcher | BrowserContextDispatcher, cdpSession: CDPSession) {
|
||||||
super(scope, cdpSession, 'CDPSession', {});
|
super(scope, cdpSession, 'CDPSession', {});
|
||||||
this.addObjectListener(CDPSession.Events.Event, ({ method, params }) => this._dispatchEvent('event', { method, params }));
|
this.addObjectListener('event', ({ method, params }) => this._dispatchEvent('event', { method, params }));
|
||||||
this.addObjectListener(CDPSession.Events.Closed, () => this._dispose());
|
this.addObjectListener('closed', () => this._dispose());
|
||||||
}
|
}
|
||||||
|
|
||||||
async send(params: channels.CDPSessionSendParams): Promise<channels.CDPSessionSendResult> {
|
async send(params: channels.CDPSessionSendParams): Promise<channels.CDPSessionSendResult> {
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@
|
||||||
import type * as channels from '@protocol/channels';
|
import type * as channels from '@protocol/channels';
|
||||||
import { eventsHelper } from '../../utils';
|
import { eventsHelper } from '../../utils';
|
||||||
import type { RegisteredListener } from '../../utils/eventsHelper';
|
import type { RegisteredListener } from '../../utils/eventsHelper';
|
||||||
import { DebugController } from '../debugController';
|
import type { DebugController } from '../debugController';
|
||||||
import type { DispatcherConnection, RootDispatcher } from './dispatcher';
|
import type { DispatcherConnection, RootDispatcher } from './dispatcher';
|
||||||
import { Dispatcher } from './dispatcher';
|
import { Dispatcher } from './dispatcher';
|
||||||
|
|
||||||
|
|
@ -29,19 +29,19 @@ export class DebugControllerDispatcher extends Dispatcher<DebugController, chann
|
||||||
super(connection, debugController, 'DebugController', {});
|
super(connection, debugController, 'DebugController', {});
|
||||||
this._type_DebugController = true;
|
this._type_DebugController = true;
|
||||||
this._listeners = [
|
this._listeners = [
|
||||||
eventsHelper.addEventListener(this._object, DebugController.Events.StateChanged, params => {
|
this._object.addManagedListener('stateChanged', params => {
|
||||||
this._dispatchEvent('stateChanged', params);
|
this._dispatchEvent('stateChanged', params);
|
||||||
}),
|
}),
|
||||||
eventsHelper.addEventListener(this._object, DebugController.Events.InspectRequested, ({ selector, locator }) => {
|
this._object.addManagedListener('inspectRequested', ({ selector, locator }) => {
|
||||||
this._dispatchEvent('inspectRequested', { selector, locator });
|
this._dispatchEvent('inspectRequested', { selector, locator });
|
||||||
}),
|
}),
|
||||||
eventsHelper.addEventListener(this._object, DebugController.Events.SourceChanged, ({ text, header, footer, actions }) => {
|
this._object.addManagedListener('sourceChanged', ({ text, header, footer, actions }) => {
|
||||||
this._dispatchEvent('sourceChanged', ({ text, header, footer, actions }));
|
this._dispatchEvent('sourceChanged', ({ text, header, footer, actions }));
|
||||||
}),
|
}),
|
||||||
eventsHelper.addEventListener(this._object, DebugController.Events.Paused, ({ paused }) => {
|
this._object.addManagedListener('paused', ({ paused }) => {
|
||||||
this._dispatchEvent('paused', ({ paused }));
|
this._dispatchEvent('paused', ({ paused }));
|
||||||
}),
|
}),
|
||||||
eventsHelper.addEventListener(this._object, DebugController.Events.SetModeRequested, ({ mode }) => {
|
this._object.addManagedListener('setModeRequested', ({ mode }) => {
|
||||||
this._dispatchEvent('setModeRequested', ({ mode }));
|
this._dispatchEvent('setModeRequested', ({ mode }));
|
||||||
}),
|
}),
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ import type { CallMetadata } from '../instrumentation';
|
||||||
import { SdkObject } from '../instrumentation';
|
import { SdkObject } from '../instrumentation';
|
||||||
import type { PlaywrightDispatcher } from './playwrightDispatcher';
|
import type { PlaywrightDispatcher } from './playwrightDispatcher';
|
||||||
import { eventsHelper } from '../..//utils/eventsHelper';
|
import { eventsHelper } from '../..//utils/eventsHelper';
|
||||||
import type { RegisteredListener } from '../..//utils/eventsHelper';
|
import type { ManagedEventEmitter, RegisteredListener } from '../..//utils/eventsHelper';
|
||||||
import { isProtocolError } from '../protocolError';
|
import { isProtocolError } from '../protocolError';
|
||||||
|
|
||||||
export const dispatcherSymbol = Symbol('dispatcher');
|
export const dispatcherSymbol = Symbol('dispatcher');
|
||||||
|
|
@ -44,7 +44,7 @@ function maxDispatchersForBucket(gcBucket: string) {
|
||||||
}[gcBucket] ?? 10000;
|
}[gcBucket] ?? 10000;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Dispatcher<Type extends { guid: string }, ChannelType, ParentScopeType extends DispatcherScope> extends EventEmitter implements channels.Channel {
|
export class Dispatcher<Type extends (ManagedEventEmitter<any> & { guid: string }) | { guid: string; _events?: void }, ChannelType, ParentScopeType extends DispatcherScope> extends EventEmitter implements channels.Channel {
|
||||||
private _connection: DispatcherConnection;
|
private _connection: DispatcherConnection;
|
||||||
// Parent is always "isScope".
|
// Parent is always "isScope".
|
||||||
private _parent: ParentScopeType | undefined;
|
private _parent: ParentScopeType | undefined;
|
||||||
|
|
@ -88,8 +88,12 @@ export class Dispatcher<Type extends { guid: string }, ChannelType, ParentScopeT
|
||||||
return this._parent!;
|
return this._parent!;
|
||||||
}
|
}
|
||||||
|
|
||||||
addObjectListener(eventName: (string | symbol), handler: (...args: any[]) => void) {
|
addObjectListener<K extends keyof Type['_events']>(
|
||||||
this._eventListeners.push(eventsHelper.addEventListener(this._object as unknown as EventEmitter, eventName, handler));
|
eventName: K,
|
||||||
|
handler: (...args: Type['_events'][K]) => void
|
||||||
|
) {
|
||||||
|
// @ts-ignore
|
||||||
|
this._eventListeners.push(eventsHelper.addEventListener(this._object, eventName, handler));
|
||||||
}
|
}
|
||||||
|
|
||||||
adopt(child: DispatcherScope) {
|
adopt(child: DispatcherScope) {
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@
|
||||||
import type { RootDispatcher } from './dispatcher';
|
import type { RootDispatcher } from './dispatcher';
|
||||||
import { Dispatcher } from './dispatcher';
|
import { Dispatcher } from './dispatcher';
|
||||||
import type { Electron } from '../electron/electron';
|
import type { Electron } from '../electron/electron';
|
||||||
import { ElectronApplication } from '../electron/electron';
|
import type { ElectronApplication } from '../electron/electron';
|
||||||
import type * as channels from '@protocol/channels';
|
import type * as channels from '@protocol/channels';
|
||||||
import { BrowserContextDispatcher } from './browserContextDispatcher';
|
import { BrowserContextDispatcher } from './browserContextDispatcher';
|
||||||
import type { PageDispatcher } from './pageDispatcher';
|
import type { PageDispatcher } from './pageDispatcher';
|
||||||
|
|
@ -47,11 +47,11 @@ export class ElectronApplicationDispatcher extends Dispatcher<ElectronApplicatio
|
||||||
super(scope, electronApplication, 'ElectronApplication', {
|
super(scope, electronApplication, 'ElectronApplication', {
|
||||||
context: new BrowserContextDispatcher(scope, electronApplication.context())
|
context: new BrowserContextDispatcher(scope, electronApplication.context())
|
||||||
});
|
});
|
||||||
this.addObjectListener(ElectronApplication.Events.Close, () => {
|
this.addObjectListener('close', () => {
|
||||||
this._dispatchEvent('close');
|
this._dispatchEvent('close');
|
||||||
this._dispose();
|
this._dispose();
|
||||||
});
|
});
|
||||||
this.addObjectListener(ElectronApplication.Events.Console, (message: ConsoleMessage) => {
|
this.addObjectListener('console', (message: ConsoleMessage) => {
|
||||||
if (!this._subscriptions.has('console'))
|
if (!this._subscriptions.has('console'))
|
||||||
return;
|
return;
|
||||||
this._dispatchEvent('console', {
|
this._dispatchEvent('console', {
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { NavigationEvent } from '../frames';
|
import type { NavigationEvent } from '../frames';
|
||||||
import { Frame } from '../frames';
|
import type { Frame } from '../frames';
|
||||||
import type * as channels from '@protocol/channels';
|
import type * as channels from '@protocol/channels';
|
||||||
import { Dispatcher, existingDispatcher } from './dispatcher';
|
import { Dispatcher, existingDispatcher } from './dispatcher';
|
||||||
import { ElementHandleDispatcher } from './elementHandlerDispatcher';
|
import { ElementHandleDispatcher } from './elementHandlerDispatcher';
|
||||||
|
|
@ -58,13 +58,13 @@ export class FrameDispatcher extends Dispatcher<Frame, channels.FrameChannel, Br
|
||||||
}, gcBucket);
|
}, gcBucket);
|
||||||
this._browserContextDispatcher = scope;
|
this._browserContextDispatcher = scope;
|
||||||
this._frame = frame;
|
this._frame = frame;
|
||||||
this.addObjectListener(Frame.Events.AddLifecycle, lifecycleEvent => {
|
this.addObjectListener('addlifecycle', lifecycleEvent => {
|
||||||
this._dispatchEvent('loadstate', { add: lifecycleEvent });
|
this._dispatchEvent('loadstate', { add: lifecycleEvent });
|
||||||
});
|
});
|
||||||
this.addObjectListener(Frame.Events.RemoveLifecycle, lifecycleEvent => {
|
this.addObjectListener('removelifecycle', lifecycleEvent => {
|
||||||
this._dispatchEvent('loadstate', { remove: lifecycleEvent });
|
this._dispatchEvent('loadstate', { remove: lifecycleEvent });
|
||||||
});
|
});
|
||||||
this.addObjectListener(Frame.Events.InternalNavigation, (event: NavigationEvent) => {
|
this.addObjectListener('internalnavigation', (event: NavigationEvent) => {
|
||||||
if (!event.isPublic)
|
if (!event.isPublic)
|
||||||
return;
|
return;
|
||||||
const params = { url: event.url, name: event.name, error: event.error ? event.error.message : undefined };
|
const params = { url: event.url, name: event.name, error: event.error ? event.error.message : undefined };
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ import type * as channels from '@protocol/channels';
|
||||||
import type { APIRequestContext } from '../fetch';
|
import type { APIRequestContext } from '../fetch';
|
||||||
import type { CallMetadata } from '../instrumentation';
|
import type { CallMetadata } from '../instrumentation';
|
||||||
import type { Request, Response, Route } from '../network';
|
import type { Request, Response, Route } from '../network';
|
||||||
import { WebSocket } from '../network';
|
import type { WebSocket } from '../network';
|
||||||
import type { RootDispatcher } from './dispatcher';
|
import type { RootDispatcher } from './dispatcher';
|
||||||
import { Dispatcher, existingDispatcher } from './dispatcher';
|
import { Dispatcher, existingDispatcher } from './dispatcher';
|
||||||
import { TracingDispatcher } from './tracingDispatcher';
|
import { TracingDispatcher } from './tracingDispatcher';
|
||||||
|
|
@ -164,10 +164,10 @@ export class WebSocketDispatcher extends Dispatcher<WebSocket, channels.WebSocke
|
||||||
super(scope, webSocket, 'WebSocket', {
|
super(scope, webSocket, 'WebSocket', {
|
||||||
url: webSocket.url(),
|
url: webSocket.url(),
|
||||||
});
|
});
|
||||||
this.addObjectListener(WebSocket.Events.FrameSent, (event: { opcode: number, data: string }) => this._dispatchEvent('frameSent', event));
|
this.addObjectListener('framesent', (event: { opcode: number, data: string }) => this._dispatchEvent('frameSent', event));
|
||||||
this.addObjectListener(WebSocket.Events.FrameReceived, (event: { opcode: number, data: string }) => this._dispatchEvent('frameReceived', event));
|
this.addObjectListener('framereceived', (event: { opcode: number, data: string }) => this._dispatchEvent('frameReceived', event));
|
||||||
this.addObjectListener(WebSocket.Events.SocketError, (error: string) => this._dispatchEvent('socketError', { error }));
|
this.addObjectListener('socketerror', (error: string) => this._dispatchEvent('socketError', { error }));
|
||||||
this.addObjectListener(WebSocket.Events.Close, () => this._dispatchEvent('close', {}));
|
this.addObjectListener('close', () => this._dispatchEvent('close', {}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,8 @@
|
||||||
|
|
||||||
import type { BrowserContext } from '../browserContext';
|
import type { BrowserContext } from '../browserContext';
|
||||||
import type { Frame } from '../frames';
|
import type { Frame } from '../frames';
|
||||||
import { Page, Worker } from '../page';
|
import type { Worker } from '../page';
|
||||||
|
import type { Page } from '../page';
|
||||||
import type * as channels from '@protocol/channels';
|
import type * as channels from '@protocol/channels';
|
||||||
import { Dispatcher, existingDispatcher } from './dispatcher';
|
import { Dispatcher, existingDispatcher } from './dispatcher';
|
||||||
import { parseError } from '../errors';
|
import { parseError } from '../errors';
|
||||||
|
|
@ -70,25 +71,25 @@ export class PageDispatcher extends Dispatcher<Page, channels.PageChannel, Brows
|
||||||
this.adopt(mainFrame);
|
this.adopt(mainFrame);
|
||||||
|
|
||||||
this._page = page;
|
this._page = page;
|
||||||
this.addObjectListener(Page.Events.Close, () => {
|
this.addObjectListener('close', () => {
|
||||||
this._dispatchEvent('close');
|
this._dispatchEvent('close');
|
||||||
this._dispose();
|
this._dispose();
|
||||||
});
|
});
|
||||||
this.addObjectListener(Page.Events.Crash, () => this._dispatchEvent('crash'));
|
this.addObjectListener('crash', () => this._dispatchEvent('crash'));
|
||||||
this.addObjectListener(Page.Events.Download, (download: Download) => {
|
this.addObjectListener('download', (download: Download) => {
|
||||||
// Artifact can outlive the page, so bind to the context scope.
|
// Artifact can outlive the page, so bind to the context scope.
|
||||||
this._dispatchEvent('download', { url: download.url, suggestedFilename: download.suggestedFilename(), artifact: ArtifactDispatcher.from(parentScope, download.artifact) });
|
this._dispatchEvent('download', { url: download.url, suggestedFilename: download.suggestedFilename(), artifact: ArtifactDispatcher.from(parentScope, download.artifact) });
|
||||||
});
|
});
|
||||||
this.addObjectListener(Page.Events.FileChooser, (fileChooser: FileChooser) => this._dispatchEvent('fileChooser', {
|
this.addObjectListener('filechooser', (fileChooser: FileChooser) => this._dispatchEvent('fileChooser', {
|
||||||
element: ElementHandleDispatcher.from(mainFrame, fileChooser.element()),
|
element: ElementHandleDispatcher.from(mainFrame, fileChooser.element()),
|
||||||
isMultiple: fileChooser.isMultiple()
|
isMultiple: fileChooser.isMultiple()
|
||||||
}));
|
}));
|
||||||
this.addObjectListener(Page.Events.FrameAttached, frame => this._onFrameAttached(frame));
|
this.addObjectListener('frameattached', frame => this._onFrameAttached(frame));
|
||||||
this.addObjectListener(Page.Events.FrameDetached, frame => this._onFrameDetached(frame));
|
this.addObjectListener('framedetached', frame => this._onFrameDetached(frame));
|
||||||
this.addObjectListener(Page.Events.LocatorHandlerTriggered, (uid: number) => this._dispatchEvent('locatorHandlerTriggered', { uid }));
|
this.addObjectListener('locatorhandlertriggered', (uid: number) => this._dispatchEvent('locatorHandlerTriggered', { uid }));
|
||||||
this.addObjectListener(Page.Events.WebSocket, webSocket => this._dispatchEvent('webSocket', { webSocket: new WebSocketDispatcher(this, webSocket) }));
|
this.addObjectListener('websocket', webSocket => this._dispatchEvent('webSocket', { webSocket: new WebSocketDispatcher(this, webSocket) }));
|
||||||
this.addObjectListener(Page.Events.Worker, worker => this._dispatchEvent('worker', { worker: new WorkerDispatcher(this, worker) }));
|
this.addObjectListener('worker', worker => this._dispatchEvent('worker', { worker: new WorkerDispatcher(this, worker) }));
|
||||||
this.addObjectListener(Page.Events.Video, (artifact: Artifact) => this._dispatchEvent('video', { artifact: ArtifactDispatcher.from(parentScope, artifact) }));
|
this.addObjectListener('video', (artifact: Artifact) => this._dispatchEvent('video', { artifact: ArtifactDispatcher.from(parentScope, artifact) }));
|
||||||
if (page._video)
|
if (page._video)
|
||||||
this._dispatchEvent('video', { artifact: ArtifactDispatcher.from(this.parentScope(), page._video) });
|
this._dispatchEvent('video', { artifact: ArtifactDispatcher.from(this.parentScope(), page._video) });
|
||||||
// Ensure client knows about all frames.
|
// Ensure client knows about all frames.
|
||||||
|
|
@ -338,7 +339,7 @@ export class WorkerDispatcher extends Dispatcher<Worker, channels.WorkerChannel,
|
||||||
super(scope, worker, 'Worker', {
|
super(scope, worker, 'Worker', {
|
||||||
url: worker.url()
|
url: worker.url()
|
||||||
});
|
});
|
||||||
this.addObjectListener(Worker.Events.Close, () => this._dispatchEvent('close'));
|
this.addObjectListener('close', () => this._dispatchEvent('close'));
|
||||||
}
|
}
|
||||||
|
|
||||||
async evaluateExpression(params: channels.WorkerEvaluateExpressionParams, metadata: CallMetadata): Promise<channels.WorkerEvaluateExpressionResult> {
|
async evaluateExpression(params: channels.WorkerEvaluateExpressionParams, metadata: CallMetadata): Promise<channels.WorkerEvaluateExpressionResult> {
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ import type { Browser } from '../browser';
|
||||||
import { GlobalAPIRequestContext } from '../fetch';
|
import { GlobalAPIRequestContext } from '../fetch';
|
||||||
import type { Playwright } from '../playwright';
|
import type { Playwright } from '../playwright';
|
||||||
import type { SocksSocketClosedPayload, SocksSocketDataPayload, SocksSocketRequestedPayload } from '../../common/socksProxy';
|
import type { SocksSocketClosedPayload, SocksSocketDataPayload, SocksSocketRequestedPayload } from '../../common/socksProxy';
|
||||||
import { SocksProxy } from '../../common/socksProxy';
|
import type { SocksProxy } from '../../common/socksProxy';
|
||||||
import { AndroidDispatcher } from './androidDispatcher';
|
import { AndroidDispatcher } from './androidDispatcher';
|
||||||
import { BrowserTypeDispatcher } from './browserTypeDispatcher';
|
import { BrowserTypeDispatcher } from './browserTypeDispatcher';
|
||||||
import type { RootDispatcher } from './dispatcher';
|
import type { RootDispatcher } from './dispatcher';
|
||||||
|
|
@ -79,9 +79,9 @@ class SocksSupportDispatcher extends Dispatcher<{ guid: string }, channels.Socks
|
||||||
this._type_SocksSupport = true;
|
this._type_SocksSupport = true;
|
||||||
this._socksProxy = socksProxy;
|
this._socksProxy = socksProxy;
|
||||||
this._socksListeners = [
|
this._socksListeners = [
|
||||||
eventsHelper.addEventListener(socksProxy, SocksProxy.Events.SocksRequested, (payload: SocksSocketRequestedPayload) => this._dispatchEvent('socksRequested', payload)),
|
socksProxy.addManagedListener('socksRequested', (payload: SocksSocketRequestedPayload) => this._dispatchEvent('socksRequested', payload)),
|
||||||
eventsHelper.addEventListener(socksProxy, SocksProxy.Events.SocksData, (payload: SocksSocketDataPayload) => this._dispatchEvent('socksData', payload)),
|
socksProxy.addManagedListener('socksData', (payload: SocksSocketDataPayload) => this._dispatchEvent('socksData', payload)),
|
||||||
eventsHelper.addEventListener(socksProxy, SocksProxy.Events.SocksClosed, (payload: SocksSocketClosedPayload) => this._dispatchEvent('socksClosed', payload)),
|
socksProxy.addManagedListener('socksClosed', (payload: SocksSocketClosedPayload) => this._dispatchEvent('socksClosed', payload)),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import { Page } from './page';
|
import type { Page } from './page';
|
||||||
import { assert } from '../utils';
|
import { assert } from '../utils';
|
||||||
import { Artifact } from './artifact';
|
import { Artifact } from './artifact';
|
||||||
|
|
||||||
|
|
@ -35,13 +35,13 @@ export class Download {
|
||||||
this._suggestedFilename = suggestedFilename;
|
this._suggestedFilename = suggestedFilename;
|
||||||
page._browserContext._downloads.add(this);
|
page._browserContext._downloads.add(this);
|
||||||
if (suggestedFilename !== undefined)
|
if (suggestedFilename !== undefined)
|
||||||
this._page.emit(Page.Events.Download, this);
|
this._page.emit('download', this);
|
||||||
}
|
}
|
||||||
|
|
||||||
_filenameSuggested(suggestedFilename: string) {
|
_filenameSuggested(suggestedFilename: string) {
|
||||||
assert(this._suggestedFilename === undefined);
|
assert(this._suggestedFilename === undefined);
|
||||||
this._suggestedFilename = suggestedFilename;
|
this._suggestedFilename = suggestedFilename;
|
||||||
this._page.emit(Page.Events.Download, this);
|
this._page.emit('download', this);
|
||||||
}
|
}
|
||||||
|
|
||||||
suggestedFilename(): string {
|
suggestedFilename(): string {
|
||||||
|
|
|
||||||
|
|
@ -49,12 +49,10 @@ import { ConsoleMessage } from '../console';
|
||||||
|
|
||||||
const ARTIFACTS_FOLDER = path.join(os.tmpdir(), 'playwright-artifacts-');
|
const ARTIFACTS_FOLDER = path.join(os.tmpdir(), 'playwright-artifacts-');
|
||||||
|
|
||||||
export class ElectronApplication extends SdkObject {
|
export class ElectronApplication extends SdkObject<{
|
||||||
static Events = {
|
close: [],
|
||||||
Close: 'close',
|
console: [ConsoleMessage]
|
||||||
Console: 'console',
|
}> {
|
||||||
};
|
|
||||||
|
|
||||||
private _browserContext: CRBrowserContext;
|
private _browserContext: CRBrowserContext;
|
||||||
private _nodeConnection: CRConnection;
|
private _nodeConnection: CRConnection;
|
||||||
private _nodeSession: CRSession;
|
private _nodeSession: CRSession;
|
||||||
|
|
@ -83,7 +81,7 @@ export class ElectronApplication extends SdkObject {
|
||||||
this._nodeElectronHandlePromise.resolve(new js.JSHandle(this._nodeExecutionContext!, 'object', 'ElectronModule', remoteObject.objectId!));
|
this._nodeElectronHandlePromise.resolve(new js.JSHandle(this._nodeExecutionContext!, 'object', 'ElectronModule', remoteObject.objectId!));
|
||||||
});
|
});
|
||||||
this._nodeSession.on('Runtime.consoleAPICalled', event => this._onConsoleAPI(event));
|
this._nodeSession.on('Runtime.consoleAPICalled', event => this._onConsoleAPI(event));
|
||||||
const appClosePromise = new Promise(f => this.once(ElectronApplication.Events.Close, f));
|
const appClosePromise = new Promise<void>(f => this.once('close', f));
|
||||||
this._browserContext.setCustomCloseHandler(async () => {
|
this._browserContext.setCustomCloseHandler(async () => {
|
||||||
await this._browserContext.stopVideoRecording();
|
await this._browserContext.stopVideoRecording();
|
||||||
const electronHandle = await this._nodeElectronHandlePromise;
|
const electronHandle = await this._nodeElectronHandlePromise;
|
||||||
|
|
@ -114,7 +112,7 @@ export class ElectronApplication extends SdkObject {
|
||||||
return;
|
return;
|
||||||
const args = event.args.map(arg => this._nodeExecutionContext!.createHandle(arg));
|
const args = event.args.map(arg => this._nodeExecutionContext!.createHandle(arg));
|
||||||
const message = new ConsoleMessage(null, event.type, undefined, args, toConsoleMessageLocation(event.stackTrace));
|
const message = new ConsoleMessage(null, event.type, undefined, args, toConsoleMessageLocation(event.stackTrace));
|
||||||
this.emit(ElectronApplication.Events.Console, message);
|
this.emit('console', message);
|
||||||
}
|
}
|
||||||
|
|
||||||
async initialize() {
|
async initialize() {
|
||||||
|
|
@ -226,7 +224,7 @@ export class Electron extends SdkObject {
|
||||||
handleSIGINT: true,
|
handleSIGINT: true,
|
||||||
handleSIGTERM: true,
|
handleSIGTERM: true,
|
||||||
handleSIGHUP: true,
|
handleSIGHUP: true,
|
||||||
onExit: () => app?.emit(ElectronApplication.Events.Close),
|
onExit: () => app?.emit('close'),
|
||||||
});
|
});
|
||||||
|
|
||||||
// All waitForLines must be started immediately.
|
// All waitForLines must be started immediately.
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,8 @@ import { TimeoutSettings } from '../common/timeoutSettings';
|
||||||
import { getUserAgent } from '../utils/userAgent';
|
import { getUserAgent } from '../utils/userAgent';
|
||||||
import { assert, createGuid, monotonicTime } from '../utils';
|
import { assert, createGuid, monotonicTime } from '../utils';
|
||||||
import { HttpsProxyAgent, SocksProxyAgent } from '../utilsBundle';
|
import { HttpsProxyAgent, SocksProxyAgent } from '../utilsBundle';
|
||||||
import { BrowserContext, verifyClientCertificates } from './browserContext';
|
import type { BrowserContext } from './browserContext';
|
||||||
|
import { verifyClientCertificates } from './browserContext';
|
||||||
import { CookieStore, domainMatches } from './cookieStore';
|
import { CookieStore, domainMatches } from './cookieStore';
|
||||||
import { MultipartFormData } from './formData';
|
import { MultipartFormData } from './formData';
|
||||||
import { httpHappyEyeballsAgent, httpsHappyEyeballsAgent } from '../utils/happy-eyeballs';
|
import { httpHappyEyeballsAgent, httpsHappyEyeballsAgent } from '../utils/happy-eyeballs';
|
||||||
|
|
@ -82,14 +83,11 @@ type SendRequestOptions = https.RequestOptions & {
|
||||||
__testHookLookup?: (hostname: string) => LookupAddress[]
|
__testHookLookup?: (hostname: string) => LookupAddress[]
|
||||||
};
|
};
|
||||||
|
|
||||||
export abstract class APIRequestContext extends SdkObject {
|
export abstract class APIRequestContext extends SdkObject<{
|
||||||
static Events = {
|
dispose: []
|
||||||
Dispose: 'dispose',
|
request: [APIRequestEvent]
|
||||||
|
requestfinished: [APIRequestFinishedEvent]
|
||||||
Request: 'request',
|
}> {
|
||||||
RequestFinished: 'requestfinished',
|
|
||||||
};
|
|
||||||
|
|
||||||
readonly fetchResponses: Map<string, Buffer> = new Map();
|
readonly fetchResponses: Map<string, Buffer> = new Map();
|
||||||
readonly fetchLog: Map<string, string[]> = new Map();
|
readonly fetchLog: Map<string, string[]> = new Map();
|
||||||
protected static allInstances: Set<APIRequestContext> = new Set();
|
protected static allInstances: Set<APIRequestContext> = new Set();
|
||||||
|
|
@ -114,7 +112,7 @@ export abstract class APIRequestContext extends SdkObject {
|
||||||
APIRequestContext.allInstances.delete(this);
|
APIRequestContext.allInstances.delete(this);
|
||||||
this.fetchResponses.clear();
|
this.fetchResponses.clear();
|
||||||
this.fetchLog.clear();
|
this.fetchLog.clear();
|
||||||
this.emit(APIRequestContext.Events.Dispose);
|
this.emit('dispose');
|
||||||
}
|
}
|
||||||
|
|
||||||
disposeResponse(fetchUid: string) {
|
disposeResponse(fetchUid: string) {
|
||||||
|
|
@ -289,7 +287,7 @@ export abstract class APIRequestContext extends SdkObject {
|
||||||
cookies: requestCookies,
|
cookies: requestCookies,
|
||||||
postData
|
postData
|
||||||
};
|
};
|
||||||
this.emit(APIRequestContext.Events.Request, requestEvent);
|
this.emit('request', requestEvent);
|
||||||
|
|
||||||
return new Promise((fulfill, reject) => {
|
return new Promise((fulfill, reject) => {
|
||||||
const requestConstructor: ((url: URL, options: http.RequestOptions, callback?: (res: http.IncomingMessage) => void) => http.ClientRequest)
|
const requestConstructor: ((url: URL, options: http.RequestOptions, callback?: (res: http.IncomingMessage) => void) => http.ClientRequest)
|
||||||
|
|
@ -309,7 +307,7 @@ export abstract class APIRequestContext extends SdkObject {
|
||||||
cookies,
|
cookies,
|
||||||
body
|
body
|
||||||
};
|
};
|
||||||
this.emit(APIRequestContext.Events.RequestFinished, requestFinishedEvent);
|
this.emit('requestfinished', requestFinishedEvent);
|
||||||
};
|
};
|
||||||
progress.log(`← ${response.statusCode} ${response.statusMessage}`);
|
progress.log(`← ${response.statusCode} ${response.statusMessage}`);
|
||||||
for (const [name, value] of Object.entries(response.headers))
|
for (const [name, value] of Object.entries(response.headers))
|
||||||
|
|
@ -447,8 +445,8 @@ export abstract class APIRequestContext extends SdkObject {
|
||||||
reject(new Error('Request context disposed.'));
|
reject(new Error('Request context disposed.'));
|
||||||
request.destroy();
|
request.destroy();
|
||||||
};
|
};
|
||||||
this.on(APIRequestContext.Events.Dispose, disposeListener);
|
this.on('dispose', disposeListener);
|
||||||
request.on('close', () => this.off(APIRequestContext.Events.Dispose, disposeListener));
|
request.on('close', () => this.off('dispose', disposeListener));
|
||||||
|
|
||||||
progress.log(`→ ${options.method} ${url.toString()}`);
|
progress.log(`→ ${options.method} ${url.toString()}`);
|
||||||
if (options.headers) {
|
if (options.headers) {
|
||||||
|
|
@ -508,7 +506,7 @@ export class BrowserContextAPIRequestContext extends APIRequestContext {
|
||||||
constructor(context: BrowserContext) {
|
constructor(context: BrowserContext) {
|
||||||
super(context);
|
super(context);
|
||||||
this._context = context;
|
this._context = context;
|
||||||
context.once(BrowserContext.Events.Close, () => this._disposeImpl());
|
context.once('close', () => this._disposeImpl());
|
||||||
}
|
}
|
||||||
|
|
||||||
override tracing() {
|
override tracing() {
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ import type { InitScript, 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, type FFSession } from './ffConnection';
|
import { 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';
|
||||||
|
|
@ -69,7 +69,7 @@ export class FFBrowser extends Browser {
|
||||||
this.session = connection.rootSession;
|
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('disconnected', () => this._onDisconnect());
|
||||||
this.session.on('Browser.attachedToTarget', this._onAttachedToTarget.bind(this));
|
this.session.on('Browser.attachedToTarget', this._onAttachedToTarget.bind(this));
|
||||||
this.session.on('Browser.detachedFromTarget', this._onDetachedFromTarget.bind(this));
|
this.session.on('Browser.detachedFromTarget', this._onDetachedFromTarget.bind(this));
|
||||||
this.session.on('Browser.downloadCreated', this._onDownloadCreated.bind(this));
|
this.session.on('Browser.downloadCreated', this._onDownloadCreated.bind(this));
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,6 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { EventEmitter } from 'events';
|
|
||||||
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 type { RecentLogsCollector } from '../../utils/debugLogger';
|
import type { RecentLogsCollector } from '../../utils/debugLogger';
|
||||||
|
|
@ -23,16 +22,15 @@ import { debugLogger } from '../../utils/debugLogger';
|
||||||
import type { ProtocolLogger } from '../types';
|
import type { ProtocolLogger } from '../types';
|
||||||
import { helper } from '../helper';
|
import { helper } from '../helper';
|
||||||
import { ProtocolError } from '../protocolError';
|
import { ProtocolError } from '../protocolError';
|
||||||
|
import { ManagedEventEmitter } from '../../utils';
|
||||||
export const ConnectionEvents = {
|
|
||||||
Disconnected: Symbol('Disconnected'),
|
|
||||||
};
|
|
||||||
|
|
||||||
// FFPlaywright uses this special id to issue Browser.close command which we
|
// FFPlaywright uses this special id to issue Browser.close command which we
|
||||||
// should ignore.
|
// should ignore.
|
||||||
export const kBrowserCloseMessageId = -9999;
|
export const kBrowserCloseMessageId = -9999;
|
||||||
|
|
||||||
export class FFConnection extends EventEmitter {
|
export class FFConnection extends ManagedEventEmitter<{ [K in keyof Protocol.Events]: [Protocol.Events[K]] } & {
|
||||||
|
'disconnected': [],
|
||||||
|
}> {
|
||||||
private _lastId: number;
|
private _lastId: number;
|
||||||
private _transport: ConnectionTransport;
|
private _transport: ConnectionTransport;
|
||||||
private readonly _protocolLogger: ProtocolLogger;
|
private readonly _protocolLogger: ProtocolLogger;
|
||||||
|
|
@ -83,7 +81,7 @@ export class FFConnection extends EventEmitter {
|
||||||
this._transport.onclose = undefined;
|
this._transport.onclose = undefined;
|
||||||
this._browserDisconnectedLogs = helper.formatBrowserLogs(this._browserLogsCollector.recentLogs(), reason);
|
this._browserDisconnectedLogs = helper.formatBrowserLogs(this._browserLogsCollector.recentLogs(), reason);
|
||||||
this.rootSession.dispose();
|
this.rootSession.dispose();
|
||||||
Promise.resolve().then(() => this.emit(ConnectionEvents.Disconnected));
|
Promise.resolve().then(() => this.emit('disconnected'));
|
||||||
}
|
}
|
||||||
|
|
||||||
close() {
|
close() {
|
||||||
|
|
@ -98,18 +96,13 @@ export class FFConnection extends EventEmitter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class FFSession extends EventEmitter {
|
export class FFSession extends ManagedEventEmitter<{ [K in keyof Protocol.Events]: [Protocol.Events[K]] }> {
|
||||||
_connection: FFConnection;
|
_connection: FFConnection;
|
||||||
_disposed = false;
|
_disposed = false;
|
||||||
private _callbacks: Map<number, { resolve: Function, reject: Function, error: ProtocolError }>;
|
private _callbacks: Map<number, { resolve: Function, reject: Function, error: ProtocolError }>;
|
||||||
private _sessionId: string;
|
private _sessionId: string;
|
||||||
private _rawSend: (message: any) => void;
|
private _rawSend: (message: any) => void;
|
||||||
private _crashed: boolean = false;
|
private _crashed: boolean = false;
|
||||||
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(connection: FFConnection, sessionId: string, rawSend: (message: any) => void) {
|
constructor(connection: FFConnection, sessionId: string, rawSend: (message: any) => void) {
|
||||||
super();
|
super();
|
||||||
|
|
@ -161,6 +154,7 @@ export class FFSession extends EventEmitter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
// @ts-expect-error
|
||||||
Promise.resolve().then(() => this.emit(object.method!, object.params));
|
Promise.resolve().then(() => this.emit(object.method!, object.params));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -38,10 +38,10 @@ export class FFNetworkManager {
|
||||||
this._page = page;
|
this._page = page;
|
||||||
|
|
||||||
this._eventListeners = [
|
this._eventListeners = [
|
||||||
eventsHelper.addEventListener(session, 'Network.requestWillBeSent', this._onRequestWillBeSent.bind(this)),
|
session.addManagedListener('Network.requestWillBeSent', this._onRequestWillBeSent.bind(this)),
|
||||||
eventsHelper.addEventListener(session, 'Network.responseReceived', this._onResponseReceived.bind(this)),
|
session.addManagedListener('Network.responseReceived', this._onResponseReceived.bind(this)),
|
||||||
eventsHelper.addEventListener(session, 'Network.requestFinished', this._onRequestFinished.bind(this)),
|
session.addManagedListener('Network.requestFinished', this._onRequestFinished.bind(this)),
|
||||||
eventsHelper.addEventListener(session, 'Network.requestFailed', this._onRequestFailed.bind(this)),
|
session.addManagedListener('Network.requestFailed', this._onRequestFailed.bind(this)),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,6 @@ import type { Progress } from '../progress';
|
||||||
import { splitErrorMessage } from '../../utils/stackTrace';
|
import { splitErrorMessage } from '../../utils/stackTrace';
|
||||||
import { debugLogger } from '../../utils/debugLogger';
|
import { debugLogger } from '../../utils/debugLogger';
|
||||||
import { ManualPromise } from '../../utils/manualPromise';
|
import { ManualPromise } from '../../utils/manualPromise';
|
||||||
import { BrowserContext } from '../browserContext';
|
|
||||||
import { TargetClosedError } from '../errors';
|
import { TargetClosedError } from '../errors';
|
||||||
|
|
||||||
export const UTILITY_WORLD_NAME = '__playwright_utility_world__';
|
export const UTILITY_WORLD_NAME = '__playwright_utility_world__';
|
||||||
|
|
@ -70,36 +69,36 @@ export class FFPage implements PageDelegate {
|
||||||
this._page = new Page(this, browserContext);
|
this._page = new Page(this, browserContext);
|
||||||
this.rawMouse.setPage(this._page);
|
this.rawMouse.setPage(this._page);
|
||||||
this._networkManager = new FFNetworkManager(session, this._page);
|
this._networkManager = new FFNetworkManager(session, this._page);
|
||||||
this._page.on(Page.Events.FrameDetached, frame => this._removeContextsForFrame(frame));
|
this._page.on('framedetached', frame => this._removeContextsForFrame(frame));
|
||||||
// TODO: remove Page.willOpenNewWindowAsynchronously from the protocol.
|
// TODO: remove Page.willOpenNewWindowAsynchronously from the protocol.
|
||||||
this._eventListeners = [
|
this._eventListeners = [
|
||||||
eventsHelper.addEventListener(this._session, 'Page.eventFired', this._onEventFired.bind(this)),
|
this._session.addManagedListener('Page.eventFired', this._onEventFired.bind(this)),
|
||||||
eventsHelper.addEventListener(this._session, 'Page.frameAttached', this._onFrameAttached.bind(this)),
|
this._session.addManagedListener('Page.frameAttached', this._onFrameAttached.bind(this)),
|
||||||
eventsHelper.addEventListener(this._session, 'Page.frameDetached', this._onFrameDetached.bind(this)),
|
this._session.addManagedListener('Page.frameDetached', this._onFrameDetached.bind(this)),
|
||||||
eventsHelper.addEventListener(this._session, 'Page.navigationAborted', this._onNavigationAborted.bind(this)),
|
this._session.addManagedListener('Page.navigationAborted', this._onNavigationAborted.bind(this)),
|
||||||
eventsHelper.addEventListener(this._session, 'Page.navigationCommitted', this._onNavigationCommitted.bind(this)),
|
this._session.addManagedListener('Page.navigationCommitted', this._onNavigationCommitted.bind(this)),
|
||||||
eventsHelper.addEventListener(this._session, 'Page.navigationStarted', this._onNavigationStarted.bind(this)),
|
this._session.addManagedListener('Page.navigationStarted', this._onNavigationStarted.bind(this)),
|
||||||
eventsHelper.addEventListener(this._session, 'Page.sameDocumentNavigation', this._onSameDocumentNavigation.bind(this)),
|
this._session.addManagedListener('Page.sameDocumentNavigation', this._onSameDocumentNavigation.bind(this)),
|
||||||
eventsHelper.addEventListener(this._session, 'Runtime.executionContextCreated', this._onExecutionContextCreated.bind(this)),
|
this._session.addManagedListener('Runtime.executionContextCreated', this._onExecutionContextCreated.bind(this)),
|
||||||
eventsHelper.addEventListener(this._session, 'Runtime.executionContextDestroyed', this._onExecutionContextDestroyed.bind(this)),
|
this._session.addManagedListener('Runtime.executionContextDestroyed', this._onExecutionContextDestroyed.bind(this)),
|
||||||
eventsHelper.addEventListener(this._session, 'Runtime.executionContextsCleared', this._onExecutionContextsCleared.bind(this)),
|
this._session.addManagedListener('Runtime.executionContextsCleared', this._onExecutionContextsCleared.bind(this)),
|
||||||
eventsHelper.addEventListener(this._session, 'Page.linkClicked', event => this._onLinkClicked(event.phase)),
|
this._session.addManagedListener('Page.linkClicked', event => this._onLinkClicked(event.phase)),
|
||||||
eventsHelper.addEventListener(this._session, 'Page.uncaughtError', this._onUncaughtError.bind(this)),
|
this._session.addManagedListener('Page.uncaughtError', this._onUncaughtError.bind(this)),
|
||||||
eventsHelper.addEventListener(this._session, 'Runtime.console', this._onConsole.bind(this)),
|
this._session.addManagedListener('Runtime.console', this._onConsole.bind(this)),
|
||||||
eventsHelper.addEventListener(this._session, 'Page.dialogOpened', this._onDialogOpened.bind(this)),
|
this._session.addManagedListener('Page.dialogOpened', this._onDialogOpened.bind(this)),
|
||||||
eventsHelper.addEventListener(this._session, 'Page.bindingCalled', this._onBindingCalled.bind(this)),
|
this._session.addManagedListener('Page.bindingCalled', this._onBindingCalled.bind(this)),
|
||||||
eventsHelper.addEventListener(this._session, 'Page.fileChooserOpened', this._onFileChooserOpened.bind(this)),
|
this._session.addManagedListener('Page.fileChooserOpened', this._onFileChooserOpened.bind(this)),
|
||||||
eventsHelper.addEventListener(this._session, 'Page.workerCreated', this._onWorkerCreated.bind(this)),
|
this._session.addManagedListener('Page.workerCreated', this._onWorkerCreated.bind(this)),
|
||||||
eventsHelper.addEventListener(this._session, 'Page.workerDestroyed', this._onWorkerDestroyed.bind(this)),
|
this._session.addManagedListener('Page.workerDestroyed', this._onWorkerDestroyed.bind(this)),
|
||||||
eventsHelper.addEventListener(this._session, 'Page.dispatchMessageFromWorker', this._onDispatchMessageFromWorker.bind(this)),
|
this._session.addManagedListener('Page.dispatchMessageFromWorker', this._onDispatchMessageFromWorker.bind(this)),
|
||||||
eventsHelper.addEventListener(this._session, 'Page.crashed', this._onCrashed.bind(this)),
|
this._session.addManagedListener('Page.crashed', this._onCrashed.bind(this)),
|
||||||
eventsHelper.addEventListener(this._session, 'Page.videoRecordingStarted', this._onVideoRecordingStarted.bind(this)),
|
this._session.addManagedListener('Page.videoRecordingStarted', this._onVideoRecordingStarted.bind(this)),
|
||||||
|
|
||||||
eventsHelper.addEventListener(this._session, 'Page.webSocketCreated', this._onWebSocketCreated.bind(this)),
|
this._session.addManagedListener('Page.webSocketCreated', this._onWebSocketCreated.bind(this)),
|
||||||
eventsHelper.addEventListener(this._session, 'Page.webSocketClosed', this._onWebSocketClosed.bind(this)),
|
this._session.addManagedListener('Page.webSocketClosed', this._onWebSocketClosed.bind(this)),
|
||||||
eventsHelper.addEventListener(this._session, 'Page.webSocketFrameReceived', this._onWebSocketFrameReceived.bind(this)),
|
this._session.addManagedListener('Page.webSocketFrameReceived', this._onWebSocketFrameReceived.bind(this)),
|
||||||
eventsHelper.addEventListener(this._session, 'Page.webSocketFrameSent', this._onWebSocketFrameSent.bind(this)),
|
this._session.addManagedListener('Page.webSocketFrameSent', this._onWebSocketFrameSent.bind(this)),
|
||||||
eventsHelper.addEventListener(this._session, 'Page.screencastFrame', this._onScreencastFrame.bind(this)),
|
this._session.addManagedListener('Page.screencastFrame', this._onScreencastFrame.bind(this)),
|
||||||
|
|
||||||
];
|
];
|
||||||
this._session.once('Page.ready', async () => {
|
this._session.once('Page.ready', async () => {
|
||||||
|
|
@ -244,7 +243,7 @@ export class FFPage implements PageDelegate {
|
||||||
const error = new Error(message);
|
const error = new Error(message);
|
||||||
error.stack = params.message + '\n' + params.stack.split('\n').filter(Boolean).map(a => a.replace(/([^@]*)@(.*)/, ' at $1 ($2)')).join('\n');
|
error.stack = params.message + '\n' + params.stack.split('\n').filter(Boolean).map(a => a.replace(/([^@]*)@(.*)/, ' at $1 ($2)')).join('\n');
|
||||||
error.name = name;
|
error.name = name;
|
||||||
this._page.emitOnContextOnceInitialized(BrowserContext.Events.PageError, error, this._page);
|
this._page.emitOnContextOnceInitialized('pageerror', error, this._page);
|
||||||
}
|
}
|
||||||
|
|
||||||
_onConsole(payload: Protocol.Runtime.consolePayload) {
|
_onConsole(payload: Protocol.Runtime.consolePayload) {
|
||||||
|
|
@ -257,7 +256,7 @@ export class FFPage implements PageDelegate {
|
||||||
}
|
}
|
||||||
|
|
||||||
_onDialogOpened(params: Protocol.Page.dialogOpenedPayload) {
|
_onDialogOpened(params: Protocol.Page.dialogOpenedPayload) {
|
||||||
this._page.emitOnContext(BrowserContext.Events.Dialog, new dialog.Dialog(
|
this._page.emitOnContext('dialog', new dialog.Dialog(
|
||||||
this._page,
|
this._page,
|
||||||
params.type,
|
params.type,
|
||||||
params.message,
|
params.message,
|
||||||
|
|
@ -519,7 +518,7 @@ export class FFPage implements PageDelegate {
|
||||||
});
|
});
|
||||||
|
|
||||||
const buffer = Buffer.from(event.data, 'base64');
|
const buffer = Buffer.from(event.data, 'base64');
|
||||||
this._page.emit(Page.Events.ScreencastFrame, {
|
this._page.emit('screencastframe', {
|
||||||
buffer,
|
buffer,
|
||||||
width: event.deviceWidth,
|
width: event.deviceWidth,
|
||||||
height: event.deviceHeight,
|
height: event.deviceHeight,
|
||||||
|
|
|
||||||
|
|
@ -24,9 +24,9 @@ import { eventsHelper } from '../utils/eventsHelper';
|
||||||
import * as js from './javascript';
|
import * as js from './javascript';
|
||||||
import * as network from './network';
|
import * as network from './network';
|
||||||
import type { Dialog } from './dialog';
|
import type { Dialog } from './dialog';
|
||||||
import { Page } from './page';
|
import type { Page } from './page';
|
||||||
import * as types from './types';
|
import * as types from './types';
|
||||||
import { BrowserContext } from './browserContext';
|
import type { BrowserContext } from './browserContext';
|
||||||
import type { Progress } from './progress';
|
import type { Progress } from './progress';
|
||||||
import { ProgressController } from './progress';
|
import { ProgressController } from './progress';
|
||||||
import { LongStandingScope, assert, constructURLBasedOnBaseURL, makeWaitForNextTask, monotonicTime } from '../utils';
|
import { LongStandingScope, assert, constructURLBasedOnBaseURL, makeWaitForNextTask, monotonicTime } from '../utils';
|
||||||
|
|
@ -156,7 +156,7 @@ export class FrameManager {
|
||||||
assert(!this._frames.has(frameId));
|
assert(!this._frames.has(frameId));
|
||||||
const frame = new Frame(this._page, frameId, parentFrame);
|
const frame = new Frame(this._page, frameId, parentFrame);
|
||||||
this._frames.set(frameId, frame);
|
this._frames.set(frameId, frame);
|
||||||
this._page.emit(Page.Events.FrameAttached, frame);
|
this._page.emit('frameattached', frame);
|
||||||
return frame;
|
return frame;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -297,7 +297,7 @@ export class FrameManager {
|
||||||
route.continue(request, { isFallback: true }).catch(() => {});
|
route.continue(request, { isFallback: true }).catch(() => {});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this._page.emitOnContext(BrowserContext.Events.Request, request);
|
this._page.emitOnContext('request', request);
|
||||||
if (route) {
|
if (route) {
|
||||||
const r = new network.Route(request, route);
|
const r = new network.Route(request, route);
|
||||||
if (this._page._serverRequestInterceptor?.(r, request))
|
if (this._page._serverRequestInterceptor?.(r, request))
|
||||||
|
|
@ -313,14 +313,14 @@ export class FrameManager {
|
||||||
requestReceivedResponse(response: network.Response) {
|
requestReceivedResponse(response: network.Response) {
|
||||||
if (response.request()._isFavicon)
|
if (response.request()._isFavicon)
|
||||||
return;
|
return;
|
||||||
this._page.emitOnContext(BrowserContext.Events.Response, response);
|
this._page.emitOnContext('response', response);
|
||||||
}
|
}
|
||||||
|
|
||||||
reportRequestFinished(request: network.Request, response: network.Response | null) {
|
reportRequestFinished(request: network.Request, response: network.Response | null) {
|
||||||
this._inflightRequestFinished(request);
|
this._inflightRequestFinished(request);
|
||||||
if (request._isFavicon)
|
if (request._isFavicon)
|
||||||
return;
|
return;
|
||||||
this._page.emitOnContext(BrowserContext.Events.RequestFinished, { request, response });
|
this._page.emitOnContext('requestfinished', { request, response });
|
||||||
}
|
}
|
||||||
|
|
||||||
requestFailed(request: network.Request, canceled: boolean) {
|
requestFailed(request: network.Request, canceled: boolean) {
|
||||||
|
|
@ -334,7 +334,7 @@ export class FrameManager {
|
||||||
}
|
}
|
||||||
if (request._isFavicon)
|
if (request._isFavicon)
|
||||||
return;
|
return;
|
||||||
this._page.emitOnContext(BrowserContext.Events.RequestFailed, request);
|
this._page.emitOnContext('requestfailed', request);
|
||||||
}
|
}
|
||||||
|
|
||||||
dialogDidOpen(dialog: Dialog) {
|
dialogDidOpen(dialog: Dialog) {
|
||||||
|
|
@ -370,7 +370,7 @@ export class FrameManager {
|
||||||
frame._onDetached();
|
frame._onDetached();
|
||||||
this._frames.delete(frame._id);
|
this._frames.delete(frame._id);
|
||||||
if (!this._page.isClosed())
|
if (!this._page.isClosed())
|
||||||
this._page.emit(Page.Events.FrameDetached, frame);
|
this._page.emit('framedetached', frame);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _inflightRequestFinished(request: network.Request) {
|
private _inflightRequestFinished(request: network.Request) {
|
||||||
|
|
@ -420,7 +420,7 @@ export class FrameManager {
|
||||||
onWebSocketRequest(requestId: string) {
|
onWebSocketRequest(requestId: string) {
|
||||||
const ws = this._webSockets.get(requestId);
|
const ws = this._webSockets.get(requestId);
|
||||||
if (ws && ws.markAsNotified())
|
if (ws && ws.markAsNotified())
|
||||||
this._page.emit(Page.Events.WebSocket, ws);
|
this._page.emit('websocket', ws);
|
||||||
}
|
}
|
||||||
|
|
||||||
onWebSocketResponse(requestId: string, status: number, statusText: string) {
|
onWebSocketResponse(requestId: string, status: number, statusText: string) {
|
||||||
|
|
@ -457,17 +457,15 @@ export class FrameManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
private _fireInternalFrameNavigation(frame: Frame, event: NavigationEvent) {
|
private _fireInternalFrameNavigation(frame: Frame, event: NavigationEvent) {
|
||||||
frame.emit(Frame.Events.InternalNavigation, event);
|
frame.emit('internalnavigation', event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Frame extends SdkObject {
|
export class Frame extends SdkObject<{
|
||||||
static Events = {
|
internalnavigation: [NavigationEvent]
|
||||||
InternalNavigation: 'internalnavigation',
|
addlifecycle: [types.LifecycleEvent]
|
||||||
AddLifecycle: 'addlifecycle',
|
removelifecycle: [types.LifecycleEvent]
|
||||||
RemoveLifecycle: 'removelifecycle',
|
}> {
|
||||||
};
|
|
||||||
|
|
||||||
_id: string;
|
_id: string;
|
||||||
_firedLifecycleEvents = new Set<types.LifecycleEvent>();
|
_firedLifecycleEvents = new Set<types.LifecycleEvent>();
|
||||||
private _firedNetworkIdleSelf = false;
|
private _firedNetworkIdleSelf = false;
|
||||||
|
|
@ -517,7 +515,7 @@ export class Frame extends SdkObject {
|
||||||
if (this._firedLifecycleEvents.has(event))
|
if (this._firedLifecycleEvents.has(event))
|
||||||
return;
|
return;
|
||||||
this._firedLifecycleEvents.add(event);
|
this._firedLifecycleEvents.add(event);
|
||||||
this.emit(Frame.Events.AddLifecycle, event);
|
this.emit('addlifecycle', event);
|
||||||
if (this === this._page.mainFrame() && this._url !== 'about:blank')
|
if (this === this._page.mainFrame() && this._url !== 'about:blank')
|
||||||
debugLogger.log('api', ` "${event}" event fired`);
|
debugLogger.log('api', ` "${event}" event fired`);
|
||||||
this._page.mainFrame()._recalculateNetworkIdle();
|
this._page.mainFrame()._recalculateNetworkIdle();
|
||||||
|
|
@ -525,7 +523,7 @@ export class Frame extends SdkObject {
|
||||||
|
|
||||||
_onClearLifecycle() {
|
_onClearLifecycle() {
|
||||||
for (const event of this._firedLifecycleEvents)
|
for (const event of this._firedLifecycleEvents)
|
||||||
this.emit(Frame.Events.RemoveLifecycle, event);
|
this.emit('removelifecycle', event);
|
||||||
this._firedLifecycleEvents.clear();
|
this._firedLifecycleEvents.clear();
|
||||||
// Keep the current navigation request if any.
|
// Keep the current navigation request if any.
|
||||||
this._inflightRequests = new Set(Array.from(this._inflightRequests).filter(request => request === this._currentDocument.request));
|
this._inflightRequests = new Set(Array.from(this._inflightRequests).filter(request => request === this._currentDocument.request));
|
||||||
|
|
@ -600,7 +598,7 @@ export class Frame extends SdkObject {
|
||||||
}
|
}
|
||||||
if (isNetworkIdle && !this._firedLifecycleEvents.has('networkidle')) {
|
if (isNetworkIdle && !this._firedLifecycleEvents.has('networkidle')) {
|
||||||
this._firedLifecycleEvents.add('networkidle');
|
this._firedLifecycleEvents.add('networkidle');
|
||||||
this.emit(Frame.Events.AddLifecycle, 'networkidle');
|
this.emit('addlifecycle', 'networkidle');
|
||||||
if (this === this._page.mainFrame() && this._url !== 'about:blank')
|
if (this === this._page.mainFrame() && this._url !== 'about:blank')
|
||||||
debugLogger.log('api', ` "networkidle" event fired`);
|
debugLogger.log('api', ` "networkidle" event fired`);
|
||||||
}
|
}
|
||||||
|
|
@ -608,7 +606,7 @@ export class Frame extends SdkObject {
|
||||||
// Usually, networkidle is fired once and not removed after that.
|
// Usually, networkidle is fired once and not removed after that.
|
||||||
// However, when we clear them right before a new commit, this is allowed for a particular frame.
|
// However, when we clear them right before a new commit, this is allowed for a particular frame.
|
||||||
this._firedLifecycleEvents.delete('networkidle');
|
this._firedLifecycleEvents.delete('networkidle');
|
||||||
this.emit(Frame.Events.RemoveLifecycle, 'networkidle');
|
this.emit('removelifecycle', 'networkidle');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -661,13 +659,13 @@ export class Frame extends SdkObject {
|
||||||
}
|
}
|
||||||
url = helper.completeUserURL(url);
|
url = helper.completeUserURL(url);
|
||||||
|
|
||||||
const sameDocument = helper.waitForEvent(progress, this, Frame.Events.InternalNavigation, (e: NavigationEvent) => !e.newDocument);
|
const sameDocument = helper.waitForEvent(progress, this, 'internalnavigation', (e: NavigationEvent) => !e.newDocument);
|
||||||
const navigateResult = await this._page._delegate.navigateFrame(this, url, referer);
|
const navigateResult = await this._page._delegate.navigateFrame(this, url, referer);
|
||||||
|
|
||||||
let event: NavigationEvent;
|
let event: NavigationEvent;
|
||||||
if (navigateResult.newDocumentId) {
|
if (navigateResult.newDocumentId) {
|
||||||
sameDocument.dispose();
|
sameDocument.dispose();
|
||||||
event = await helper.waitForEvent(progress, this, Frame.Events.InternalNavigation, (event: NavigationEvent) => {
|
event = await helper.waitForEvent(progress, this, 'internalnavigation', (event: NavigationEvent) => {
|
||||||
// We are interested either in this specific document, or any other document that
|
// We are interested either in this specific document, or any other document that
|
||||||
// did commit and replaced the expected document.
|
// did commit and replaced the expected document.
|
||||||
return event.newDocument && (event.newDocument.documentId === navigateResult.newDocumentId || !event.error);
|
return event.newDocument && (event.newDocument.documentId === navigateResult.newDocumentId || !event.error);
|
||||||
|
|
@ -685,7 +683,7 @@ export class Frame extends SdkObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this._firedLifecycleEvents.has(waitUntil))
|
if (!this._firedLifecycleEvents.has(waitUntil))
|
||||||
await helper.waitForEvent(progress, this, Frame.Events.AddLifecycle, (e: types.LifecycleEvent) => e === waitUntil).promise;
|
await helper.waitForEvent(progress, this, 'addlifecycle', (e: types.LifecycleEvent) => e === waitUntil).promise;
|
||||||
|
|
||||||
const request = event.newDocument ? event.newDocument.request : undefined;
|
const request = event.newDocument ? event.newDocument.request : undefined;
|
||||||
const response = request ? request._finalRequest().response() : null;
|
const response = request ? request._finalRequest().response() : null;
|
||||||
|
|
@ -696,7 +694,7 @@ export class Frame extends SdkObject {
|
||||||
const waitUntil = verifyLifecycle('waitUntil', options.waitUntil === undefined ? 'load' : options.waitUntil);
|
const waitUntil = verifyLifecycle('waitUntil', options.waitUntil === undefined ? 'load' : options.waitUntil);
|
||||||
progress.log(`waiting for navigation until "${waitUntil}"`);
|
progress.log(`waiting for navigation until "${waitUntil}"`);
|
||||||
|
|
||||||
const navigationEvent: NavigationEvent = await helper.waitForEvent(progress, this, Frame.Events.InternalNavigation, (event: NavigationEvent) => {
|
const navigationEvent: NavigationEvent = await helper.waitForEvent(progress, this, 'internalnavigation', (event: NavigationEvent) => {
|
||||||
// Any failed navigation results in a rejection.
|
// Any failed navigation results in a rejection.
|
||||||
if (event.error)
|
if (event.error)
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -709,7 +707,7 @@ export class Frame extends SdkObject {
|
||||||
throw navigationEvent.error;
|
throw navigationEvent.error;
|
||||||
|
|
||||||
if (!this._firedLifecycleEvents.has(waitUntil))
|
if (!this._firedLifecycleEvents.has(waitUntil))
|
||||||
await helper.waitForEvent(progress, this, Frame.Events.AddLifecycle, (e: types.LifecycleEvent) => e === waitUntil).promise;
|
await helper.waitForEvent(progress, this, 'addlifecycle', (e: types.LifecycleEvent) => e === waitUntil).promise;
|
||||||
|
|
||||||
const request = navigationEvent.newDocument ? navigationEvent.newDocument.request : undefined;
|
const request = navigationEvent.newDocument ? navigationEvent.newDocument.request : undefined;
|
||||||
return request ? request._finalRequest().response() : null;
|
return request ? request._finalRequest().response() : null;
|
||||||
|
|
@ -718,7 +716,7 @@ export class Frame extends SdkObject {
|
||||||
async _waitForLoadState(progress: Progress, state: types.LifecycleEvent): Promise<void> {
|
async _waitForLoadState(progress: Progress, state: types.LifecycleEvent): Promise<void> {
|
||||||
const waitUntil = verifyLifecycle('state', state);
|
const waitUntil = verifyLifecycle('state', state);
|
||||||
if (!this._firedLifecycleEvents.has(waitUntil))
|
if (!this._firedLifecycleEvents.has(waitUntil))
|
||||||
await helper.waitForEvent(progress, this, Frame.Events.AddLifecycle, (e: types.LifecycleEvent) => e === waitUntil).promise;
|
await helper.waitForEvent(progress, this, 'addlifecycle', (e: types.LifecycleEvent) => e === waitUntil).promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
async frameElement(): Promise<dom.ElementHandle> {
|
async frameElement(): Promise<dom.ElementHandle> {
|
||||||
|
|
@ -1034,7 +1032,7 @@ export class Frame extends SdkObject {
|
||||||
let cspMessage: ConsoleMessage | undefined;
|
let cspMessage: ConsoleMessage | undefined;
|
||||||
const actionPromise = func().then(r => result = r).catch(e => error = e);
|
const actionPromise = func().then(r => result = r).catch(e => error = e);
|
||||||
const errorPromise = new Promise<void>(resolve => {
|
const errorPromise = new Promise<void>(resolve => {
|
||||||
listeners.push(eventsHelper.addEventListener(this._page._browserContext, BrowserContext.Events.Console, (message: ConsoleMessage) => {
|
listeners.push(this._page._browserContext.addManagedListener('console', (message: ConsoleMessage) => {
|
||||||
if (message.page() !== this._page || message.type() !== 'error')
|
if (message.page() !== this._page || message.type() !== 'error')
|
||||||
return;
|
return;
|
||||||
if (message.text().includes('Content-Security-Policy') || message.text().includes('Content Security Policy')) {
|
if (message.text().includes('Content-Security-Policy') || message.text().includes('Content Security Policy')) {
|
||||||
|
|
@ -1725,7 +1723,7 @@ class SignalBarrier {
|
||||||
if (frame.parentFrame())
|
if (frame.parentFrame())
|
||||||
return;
|
return;
|
||||||
this.retain();
|
this.retain();
|
||||||
const waiter = helper.waitForEvent(null, frame, Frame.Events.InternalNavigation, (e: NavigationEvent) => {
|
const waiter = helper.waitForEvent(null, frame, 'internalnavigation', (e: NavigationEvent) => {
|
||||||
if (!e.isPublic)
|
if (!e.isPublic)
|
||||||
return false;
|
return false;
|
||||||
if (!e.error && this._progress)
|
if (!e.error && this._progress)
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,6 @@ import { mime } from '../../utilsBundle';
|
||||||
import { ManualPromise } from '../../utils/manualPromise';
|
import { ManualPromise } from '../../utils/manualPromise';
|
||||||
import { getPlaywrightVersion } from '../../utils/userAgent';
|
import { getPlaywrightVersion } from '../../utils/userAgent';
|
||||||
import { urlMatches } from '../../utils/network';
|
import { urlMatches } from '../../utils/network';
|
||||||
import { Frame } from '../frames';
|
|
||||||
import type { HeadersArray, LifecycleEvent } from '../types';
|
import type { HeadersArray, LifecycleEvent } from '../types';
|
||||||
import { isTextualMimeType } from '../../utils/isomorphic/mimeType';
|
import { isTextualMimeType } from '../../utils/isomorphic/mimeType';
|
||||||
|
|
||||||
|
|
@ -93,19 +92,19 @@ export class HarTracer {
|
||||||
this._started = true;
|
this._started = true;
|
||||||
const apiRequest = this._context instanceof APIRequestContext ? this._context : this._context.fetchRequest;
|
const apiRequest = this._context instanceof APIRequestContext ? this._context : this._context.fetchRequest;
|
||||||
this._eventListeners = [
|
this._eventListeners = [
|
||||||
eventsHelper.addEventListener(apiRequest, APIRequestContext.Events.Request, (event: APIRequestEvent) => this._onAPIRequest(event)),
|
apiRequest.addManagedListener('request', (event: APIRequestEvent) => this._onAPIRequest(event)),
|
||||||
eventsHelper.addEventListener(apiRequest, APIRequestContext.Events.RequestFinished, (event: APIRequestFinishedEvent) => this._onAPIRequestFinished(event)),
|
apiRequest.addManagedListener('requestfinished', (event: APIRequestFinishedEvent) => this._onAPIRequestFinished(event)),
|
||||||
];
|
];
|
||||||
if (this._context instanceof BrowserContext) {
|
if (this._context instanceof BrowserContext) {
|
||||||
this._eventListeners.push(
|
this._eventListeners.push(
|
||||||
eventsHelper.addEventListener(this._context, BrowserContext.Events.Page, (page: Page) => this._createPageEntryIfNeeded(page)),
|
this._context.addManagedListener('page', (page: Page) => this._createPageEntryIfNeeded(page)),
|
||||||
eventsHelper.addEventListener(this._context, BrowserContext.Events.Request, (request: network.Request) => this._onRequest(request)),
|
this._context.addManagedListener('request', (request: network.Request) => this._onRequest(request)),
|
||||||
eventsHelper.addEventListener(this._context, BrowserContext.Events.RequestFinished, ({ request, response }) => this._onRequestFinished(request, response).catch(() => {})),
|
this._context.addManagedListener('requestfinished', ({ request, response }) => this._onRequestFinished(request, response).catch(() => {})),
|
||||||
eventsHelper.addEventListener(this._context, BrowserContext.Events.RequestFailed, request => this._onRequestFailed(request)),
|
this._context.addManagedListener('requestfailed', request => this._onRequestFailed(request)),
|
||||||
eventsHelper.addEventListener(this._context, BrowserContext.Events.Response, (response: network.Response) => this._onResponse(response)),
|
this._context.addManagedListener('response', (response: network.Response) => this._onResponse(response)),
|
||||||
eventsHelper.addEventListener(this._context, BrowserContext.Events.RequestAborted, request => this._onRequestAborted(request)),
|
this._context.addManagedListener('requestaborted', request => this._onRequestAborted(request)),
|
||||||
eventsHelper.addEventListener(this._context, BrowserContext.Events.RequestFulfilled, request => this._onRequestFulfilled(request)),
|
this._context.addManagedListener('requestfulfilled', request => this._onRequestFulfilled(request)),
|
||||||
eventsHelper.addEventListener(this._context, BrowserContext.Events.RequestContinued, request => this._onRequestContinued(request)),
|
this._context.addManagedListener('requestcontinued', request => this._onRequestContinued(request)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -139,7 +138,7 @@ export class HarTracer {
|
||||||
};
|
};
|
||||||
(pageEntry as any)[startedDateSymbol] = date;
|
(pageEntry as any)[startedDateSymbol] = date;
|
||||||
|
|
||||||
page.mainFrame().on(Frame.Events.AddLifecycle, (event: LifecycleEvent) => {
|
page.mainFrame().on('addlifecycle', (event: LifecycleEvent) => {
|
||||||
if (event === 'load')
|
if (event === 'load')
|
||||||
this._onLoad(page, pageEntry!);
|
this._onLoad(page, pageEntry!);
|
||||||
if (event === 'domcontentloaded')
|
if (event === 'domcontentloaded')
|
||||||
|
|
|
||||||
|
|
@ -15,11 +15,10 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { EventEmitter } from 'events';
|
|
||||||
import type * as types from './types';
|
import type * as types from './types';
|
||||||
import type { Progress } from './progress';
|
import type { Progress } from './progress';
|
||||||
import { debugLogger } from '../utils/debugLogger';
|
import { debugLogger } from '../utils/debugLogger';
|
||||||
import type { RegisteredListener } from '../utils/eventsHelper';
|
import type { ManagedEventEmitter, RegisteredListener } from '../utils/eventsHelper';
|
||||||
import { eventsHelper } from '../utils/eventsHelper';
|
import { eventsHelper } from '../utils/eventsHelper';
|
||||||
|
|
||||||
const MAX_LOG_LENGTH = process.env.MAX_LOG_LENGTH ? +process.env.MAX_LOG_LENGTH : Infinity;
|
const MAX_LOG_LENGTH = process.env.MAX_LOG_LENGTH ? +process.env.MAX_LOG_LENGTH : Infinity;
|
||||||
|
|
@ -53,10 +52,10 @@ class Helper {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
static waitForEvent(progress: Progress | null, emitter: EventEmitter, event: string | symbol, predicate?: Function): { promise: Promise<any>, dispose: () => void } {
|
static waitForEvent<T extends ManagedEventEmitter<any>, K extends keyof T['_events']>(progress: Progress | null, emitter: T, event: K, predicate?: Function): { promise: Promise<any>, dispose: () => void } {
|
||||||
const listeners: RegisteredListener[] = [];
|
const listeners: RegisteredListener[] = [];
|
||||||
const promise = new Promise((resolve, reject) => {
|
const promise = new Promise((resolve, reject) => {
|
||||||
listeners.push(eventsHelper.addEventListener(emitter, event, eventArg => {
|
listeners.push(emitter.addManagedListener(event, eventArg => {
|
||||||
try {
|
try {
|
||||||
if (predicate && !predicate(eventArg))
|
if (predicate && !predicate(eventArg))
|
||||||
return;
|
return;
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { EventEmitter } from 'events';
|
import { createGuid, ManagedEventEmitter } from '../utils';
|
||||||
import { createGuid } from '../utils';
|
|
||||||
import type { APIRequestContext } from './fetch';
|
import type { APIRequestContext } from './fetch';
|
||||||
import type { Browser } from './browser';
|
import type { Browser } from './browser';
|
||||||
import type { BrowserContext } from './browserContext';
|
import type { BrowserContext } from './browserContext';
|
||||||
|
|
@ -37,7 +36,7 @@ export type Attribution = {
|
||||||
import type { CallMetadata } from '@protocol/callMetadata';
|
import type { CallMetadata } from '@protocol/callMetadata';
|
||||||
export type { CallMetadata } from '@protocol/callMetadata';
|
export type { CallMetadata } from '@protocol/callMetadata';
|
||||||
|
|
||||||
export class SdkObject extends EventEmitter {
|
export class SdkObject<Events extends Record<string, any[]> = any> extends ManagedEventEmitter<Events> {
|
||||||
guid: string;
|
guid: string;
|
||||||
attribution: Attribution;
|
attribution: Attribution;
|
||||||
instrumentation: Instrumentation;
|
instrumentation: Instrumentation;
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,6 @@ import { SdkObject } from './instrumentation';
|
||||||
import type { HeadersArray, NameValue } from '../common/types';
|
import type { HeadersArray, NameValue } from '../common/types';
|
||||||
import { APIRequestContext } from './fetch';
|
import { APIRequestContext } from './fetch';
|
||||||
import type { NormalizedContinueOverrides } from './types';
|
import type { NormalizedContinueOverrides } from './types';
|
||||||
import { BrowserContext } from './browserContext';
|
|
||||||
|
|
||||||
export function filterCookies(cookies: channels.NetworkCookie[], urls: string[]): channels.NetworkCookie[] {
|
export function filterCookies(cookies: channels.NetworkCookie[], urls: string[]): channels.NetworkCookie[] {
|
||||||
const parsedURLs = urls.map(s => new URL(s));
|
const parsedURLs = urls.map(s => new URL(s));
|
||||||
|
|
@ -257,7 +256,7 @@ export class Route extends SdkObject {
|
||||||
|
|
||||||
async abort(errorCode: string = 'failed') {
|
async abort(errorCode: string = 'failed') {
|
||||||
this._startHandling();
|
this._startHandling();
|
||||||
this._request._context.emit(BrowserContext.Events.RequestAborted, this._request);
|
this._request._context.emit('requestaborted', this._request);
|
||||||
await this._delegate.abort(errorCode);
|
await this._delegate.abort(errorCode);
|
||||||
this._endHandling();
|
this._endHandling();
|
||||||
}
|
}
|
||||||
|
|
@ -285,7 +284,7 @@ export class Route extends SdkObject {
|
||||||
}
|
}
|
||||||
const headers = [...(overrides.headers || [])];
|
const headers = [...(overrides.headers || [])];
|
||||||
this._maybeAddCorsHeaders(headers);
|
this._maybeAddCorsHeaders(headers);
|
||||||
this._request._context.emit(BrowserContext.Events.RequestFulfilled, this._request);
|
this._request._context.emit('requestfulfilled', this._request);
|
||||||
await this._delegate.fulfill({
|
await this._delegate.fulfill({
|
||||||
status: overrides.status || 200,
|
status: overrides.status || 200,
|
||||||
headers,
|
headers,
|
||||||
|
|
@ -323,7 +322,7 @@ export class Route extends SdkObject {
|
||||||
}
|
}
|
||||||
this._request._setOverrides(overrides);
|
this._request._setOverrides(overrides);
|
||||||
if (!overrides.isFallback)
|
if (!overrides.isFallback)
|
||||||
this._request._context.emit(BrowserContext.Events.RequestContinued, this._request);
|
this._request._context.emit('requestcontinued', this._request);
|
||||||
await this._delegate.continue(this._request, overrides);
|
await this._delegate.continue(this._request, overrides);
|
||||||
this._endHandling();
|
this._endHandling();
|
||||||
}
|
}
|
||||||
|
|
@ -562,17 +561,15 @@ export class Response extends SdkObject {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class WebSocket extends SdkObject {
|
export class WebSocket extends SdkObject<{
|
||||||
|
close: []
|
||||||
|
socketerror: [string]
|
||||||
|
framereceived: [{ opcode: number, data: string }]
|
||||||
|
framesent: [{ opcode: number, data: string }]
|
||||||
|
}> {
|
||||||
private _url: string;
|
private _url: string;
|
||||||
private _notified = false;
|
private _notified = false;
|
||||||
|
|
||||||
static Events = {
|
|
||||||
Close: 'close',
|
|
||||||
SocketError: 'socketerror',
|
|
||||||
FrameReceived: 'framereceived',
|
|
||||||
FrameSent: 'framesent',
|
|
||||||
};
|
|
||||||
|
|
||||||
constructor(parent: SdkObject, url: string) {
|
constructor(parent: SdkObject, url: string) {
|
||||||
super(parent, 'ws');
|
super(parent, 'ws');
|
||||||
this._url = url;
|
this._url = url;
|
||||||
|
|
@ -593,19 +590,19 @@ export class WebSocket extends SdkObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
frameSent(opcode: number, data: string) {
|
frameSent(opcode: number, data: string) {
|
||||||
this.emit(WebSocket.Events.FrameSent, { opcode, data });
|
this.emit('framesent', { opcode, data });
|
||||||
}
|
}
|
||||||
|
|
||||||
frameReceived(opcode: number, data: string) {
|
frameReceived(opcode: number, data: string) {
|
||||||
this.emit(WebSocket.Events.FrameReceived, { opcode, data });
|
this.emit('framereceived', { opcode, data });
|
||||||
}
|
}
|
||||||
|
|
||||||
error(errorMessage: string) {
|
error(errorMessage: string) {
|
||||||
this.emit(WebSocket.Events.SocketError, errorMessage);
|
this.emit('socketerror', errorMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
closed() {
|
closed() {
|
||||||
this.emit(WebSocket.Events.Close);
|
this.emit('close');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,8 @@ import type { ScreenshotOptions } from './screenshotter';
|
||||||
import { Screenshotter, validateScreenshotOptions } from './screenshotter';
|
import { Screenshotter, validateScreenshotOptions } from './screenshotter';
|
||||||
import { TimeoutSettings } from '../common/timeoutSettings';
|
import { TimeoutSettings } from '../common/timeoutSettings';
|
||||||
import type * as types from './types';
|
import type * as types from './types';
|
||||||
import { BrowserContext } from './browserContext';
|
import type { BrowserContextEvents } from './browserContext';
|
||||||
|
import type { BrowserContext } from './browserContext';
|
||||||
import { ConsoleMessage } from './console';
|
import { ConsoleMessage } from './console';
|
||||||
import * as accessibility from './accessibility';
|
import * as accessibility from './accessibility';
|
||||||
import { FileChooser } from './fileChooser';
|
import { FileChooser } from './fileChooser';
|
||||||
|
|
@ -45,6 +46,7 @@ import { parseEvaluationResultValue, source } from './isomorphic/utilityScriptSe
|
||||||
import type { SerializedValue } from './isomorphic/utilityScriptSerializers';
|
import type { SerializedValue } from './isomorphic/utilityScriptSerializers';
|
||||||
import { TargetClosedError } from './errors';
|
import { TargetClosedError } from './errors';
|
||||||
import { asLocator } from '../utils/isomorphic/locatorGenerators';
|
import { asLocator } from '../utils/isomorphic/locatorGenerators';
|
||||||
|
import type { Download } from './download';
|
||||||
|
|
||||||
export interface PageDelegate {
|
export interface PageDelegate {
|
||||||
readonly rawMouse: input.RawMouse;
|
readonly rawMouse: input.RawMouse;
|
||||||
|
|
@ -121,26 +123,24 @@ type ExpectScreenshotOptions = ImageComparatorOptions & ScreenshotOptions & {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export class Page extends SdkObject {
|
export class Page extends SdkObject<{
|
||||||
static Events = {
|
close: []
|
||||||
Close: 'close',
|
crash: []
|
||||||
Crash: 'crash',
|
download: [Download]
|
||||||
Download: 'download',
|
filechooser: [FileChooser]
|
||||||
FileChooser: 'filechooser',
|
frameattached: [frames.Frame]
|
||||||
FrameAttached: 'frameattached',
|
framedetached: [frames.Frame]
|
||||||
FrameDetached: 'framedetached',
|
internalframenavigatedtonewdocument: [frames.Frame]
|
||||||
InternalFrameNavigatedToNewDocument: 'internalframenavigatedtonewdocument',
|
locatorhandlertriggered: [number]
|
||||||
LocatorHandlerTriggered: 'locatorhandlertriggered',
|
screencastframe: [{ buffer: Buffer, width: number, height: number, timestamp?: number }]
|
||||||
ScreencastFrame: 'screencastframe',
|
video: [Artifact]
|
||||||
Video: 'video',
|
websocket: [network.WebSocket]
|
||||||
WebSocket: 'websocket',
|
worker: [Worker]
|
||||||
Worker: 'worker',
|
}> {
|
||||||
};
|
|
||||||
|
|
||||||
private _closedState: 'open' | 'closing' | 'closed' = 'open';
|
private _closedState: 'open' | 'closing' | 'closed' = 'open';
|
||||||
private _closedPromise = new ManualPromise<void>();
|
private _closedPromise = new ManualPromise<void>();
|
||||||
private _initialized = false;
|
private _initialized = false;
|
||||||
private _eventsToEmitAfterInitialized: { event: string | symbol, args: any[] }[] = [];
|
private _eventsToEmitAfterInitialized: { event: string, args: any[] }[] = [];
|
||||||
private _crashed = false;
|
private _crashed = false;
|
||||||
readonly openScope = new LongStandingScope();
|
readonly openScope = new LongStandingScope();
|
||||||
readonly _browserContext: BrowserContext;
|
readonly _browserContext: BrowserContext;
|
||||||
|
|
@ -202,7 +202,7 @@ export class Page extends SdkObject {
|
||||||
this._opener = openerPage;
|
this._opener = openerPage;
|
||||||
}
|
}
|
||||||
|
|
||||||
reportAsNew(error: Error | undefined = undefined, contextEvent: string = BrowserContext.Events.Page) {
|
reportAsNew(error: Error | undefined = undefined, contextEvent: keyof BrowserContext['_events'] = 'page') {
|
||||||
if (error) {
|
if (error) {
|
||||||
// Initialization error could have happened because of
|
// Initialization error could have happened because of
|
||||||
// context/browser closure. Just ignore the page.
|
// context/browser closure. Just ignore the page.
|
||||||
|
|
@ -214,6 +214,7 @@ export class Page extends SdkObject {
|
||||||
this.emitOnContext(contextEvent, this);
|
this.emitOnContext(contextEvent, this);
|
||||||
|
|
||||||
for (const { event, args } of this._eventsToEmitAfterInitialized)
|
for (const { event, args } of this._eventsToEmitAfterInitialized)
|
||||||
|
// @ts-ignore
|
||||||
this._browserContext.emit(event, ...args);
|
this._browserContext.emit(event, ...args);
|
||||||
this._eventsToEmitAfterInitialized = [];
|
this._eventsToEmitAfterInitialized = [];
|
||||||
|
|
||||||
|
|
@ -221,7 +222,7 @@ export class Page extends SdkObject {
|
||||||
// in that case we fire another Close event to ensure that each reported Page will have
|
// in that case we fire another Close event to ensure that each reported Page will have
|
||||||
// corresponding Close event after it is reported on the context.
|
// corresponding Close event after it is reported on the context.
|
||||||
if (this.isClosed())
|
if (this.isClosed())
|
||||||
this.emit(Page.Events.Close);
|
this.emit('close');
|
||||||
else
|
else
|
||||||
this.instrumentation.onPageOpen(this);
|
this.instrumentation.onPageOpen(this);
|
||||||
}
|
}
|
||||||
|
|
@ -230,13 +231,14 @@ export class Page extends SdkObject {
|
||||||
return this._initialized ? this : undefined;
|
return this._initialized ? this : undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
emitOnContext(event: string | symbol, ...args: any[]) {
|
emitOnContext<K extends keyof BrowserContextEvents>(event: K, ...args: BrowserContextEvents[K]) {
|
||||||
if (this._isServerSideOnly)
|
if (this._isServerSideOnly)
|
||||||
return;
|
return;
|
||||||
|
// @ts-ignore
|
||||||
this._browserContext.emit(event, ...args);
|
this._browserContext.emit(event, ...args);
|
||||||
}
|
}
|
||||||
|
|
||||||
emitOnContextOnceInitialized(event: string | symbol, ...args: any[]) {
|
emitOnContextOnceInitialized<K extends keyof BrowserContextEvents>(event: K, ...args: BrowserContextEvents[K]) {
|
||||||
if (this._isServerSideOnly)
|
if (this._isServerSideOnly)
|
||||||
return;
|
return;
|
||||||
// Some events, like console messages, may come before page is ready.
|
// Some events, like console messages, may come before page is ready.
|
||||||
|
|
@ -244,6 +246,7 @@ export class Page extends SdkObject {
|
||||||
// and dispatch it to the client later, either on the live Page,
|
// and dispatch it to the client later, either on the live Page,
|
||||||
// or on the "errored" Page.
|
// or on the "errored" Page.
|
||||||
if (this._initialized)
|
if (this._initialized)
|
||||||
|
// @ts-ignore
|
||||||
this._browserContext.emit(event, ...args);
|
this._browserContext.emit(event, ...args);
|
||||||
else
|
else
|
||||||
this._eventsToEmitAfterInitialized.push({ event, args });
|
this._eventsToEmitAfterInitialized.push({ event, args });
|
||||||
|
|
@ -280,7 +283,7 @@ export class Page extends SdkObject {
|
||||||
this._frameThrottler.dispose();
|
this._frameThrottler.dispose();
|
||||||
assert(this._closedState !== 'closed', 'Page closed twice');
|
assert(this._closedState !== 'closed', 'Page closed twice');
|
||||||
this._closedState = 'closed';
|
this._closedState = 'closed';
|
||||||
this.emit(Page.Events.Close);
|
this.emit('close');
|
||||||
this._closedPromise.resolve();
|
this._closedPromise.resolve();
|
||||||
this.instrumentation.onPageClose(this);
|
this.instrumentation.onPageClose(this);
|
||||||
this.openScope.close(new TargetClosedError());
|
this.openScope.close(new TargetClosedError());
|
||||||
|
|
@ -289,7 +292,7 @@ export class Page extends SdkObject {
|
||||||
_didCrash() {
|
_didCrash() {
|
||||||
this._frameManager.dispose();
|
this._frameManager.dispose();
|
||||||
this._frameThrottler.dispose();
|
this._frameThrottler.dispose();
|
||||||
this.emit(Page.Events.Crash);
|
this.emit('crash');
|
||||||
this._crashed = true;
|
this._crashed = true;
|
||||||
this.instrumentation.onPageClose(this);
|
this.instrumentation.onPageClose(this);
|
||||||
this.openScope.close(new Error('Page crashed'));
|
this.openScope.close(new Error('Page crashed'));
|
||||||
|
|
@ -303,12 +306,12 @@ export class Page extends SdkObject {
|
||||||
// Frame/context may be gone during async processing. Do not throw.
|
// Frame/context may be gone during async processing. Do not throw.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!this.listenerCount(Page.Events.FileChooser)) {
|
if (!this.listenerCount('filechooser')) {
|
||||||
handle.dispose();
|
handle.dispose();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const fileChooser = new FileChooser(this, handle, multiple);
|
const fileChooser = new FileChooser(this, handle, multiple);
|
||||||
this.emit(Page.Events.FileChooser, fileChooser);
|
this.emit('filechooser', fileChooser);
|
||||||
}
|
}
|
||||||
|
|
||||||
context(): BrowserContext {
|
context(): BrowserContext {
|
||||||
|
|
@ -375,7 +378,7 @@ export class Page extends SdkObject {
|
||||||
args.forEach(arg => arg.dispose());
|
args.forEach(arg => arg.dispose());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.emitOnContextOnceInitialized(BrowserContext.Events.Console, message);
|
this.emitOnContextOnceInitialized('console', message);
|
||||||
}
|
}
|
||||||
|
|
||||||
async reload(metadata: CallMetadata, options: types.NavigateOptions): Promise<network.Response | null> {
|
async reload(metadata: CallMetadata, options: types.NavigateOptions): Promise<network.Response | null> {
|
||||||
|
|
@ -460,7 +463,7 @@ export class Page extends SdkObject {
|
||||||
if (!handler.resolved) {
|
if (!handler.resolved) {
|
||||||
if (await this.mainFrame().isVisibleInternal(handler.selector, { strict: true })) {
|
if (await this.mainFrame().isVisibleInternal(handler.selector, { strict: true })) {
|
||||||
handler.resolved = new ManualPromise();
|
handler.resolved = new ManualPromise();
|
||||||
this.emit(Page.Events.LocatorHandlerTriggered, uid);
|
this.emit('locatorhandlertriggered', uid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (handler.resolved) {
|
if (handler.resolved) {
|
||||||
|
|
@ -693,7 +696,7 @@ export class Page extends SdkObject {
|
||||||
|
|
||||||
_addWorker(workerId: string, worker: Worker) {
|
_addWorker(workerId: string, worker: Worker) {
|
||||||
this._workers.set(workerId, worker);
|
this._workers.set(workerId, worker);
|
||||||
this.emit(Page.Events.Worker, worker);
|
this.emit('worker', worker);
|
||||||
}
|
}
|
||||||
|
|
||||||
_removeWorker(workerId: string) {
|
_removeWorker(workerId: string) {
|
||||||
|
|
@ -721,7 +724,7 @@ export class Page extends SdkObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
frameNavigatedToNewDocument(frame: frames.Frame) {
|
frameNavigatedToNewDocument(frame: frames.Frame) {
|
||||||
this.emit(Page.Events.InternalFrameNavigatedToNewDocument, frame);
|
this.emit('internalframenavigatedtonewdocument', frame);
|
||||||
const origin = frame.origin();
|
const origin = frame.origin();
|
||||||
if (origin)
|
if (origin)
|
||||||
this._browserContext.addVisitedOrigin(origin);
|
this._browserContext.addVisitedOrigin(origin);
|
||||||
|
|
@ -769,11 +772,9 @@ export class Page extends SdkObject {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Worker extends SdkObject {
|
export class Worker extends SdkObject<{
|
||||||
static Events = {
|
close: [Worker]
|
||||||
Close: 'close',
|
}> {
|
||||||
};
|
|
||||||
|
|
||||||
private _url: string;
|
private _url: string;
|
||||||
private _executionContextPromise: Promise<js.ExecutionContext>;
|
private _executionContextPromise: Promise<js.ExecutionContext>;
|
||||||
private _executionContextCallback: (value: js.ExecutionContext) => void;
|
private _executionContextCallback: (value: js.ExecutionContext) => void;
|
||||||
|
|
@ -799,7 +800,7 @@ export class Worker extends SdkObject {
|
||||||
didClose() {
|
didClose() {
|
||||||
if (this._existingExecutionContext)
|
if (this._existingExecutionContext)
|
||||||
this._existingExecutionContext.contextDestroyed('Worker was closed');
|
this._existingExecutionContext.contextDestroyed('Worker was closed');
|
||||||
this.emit(Worker.Events.Close, this);
|
this.emit('close', this);
|
||||||
this.openScope.close(new Error('Worker closed'));
|
this.openScope.close(new Error('Worker closed'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,9 +20,9 @@ import type * as channels from '@protocol/channels';
|
||||||
import type { ActionInContext } from './recorder/codeGenerator';
|
import type { ActionInContext } from './recorder/codeGenerator';
|
||||||
import { CodeGenerator } from './recorder/codeGenerator';
|
import { CodeGenerator } from './recorder/codeGenerator';
|
||||||
import { toClickOptions, toModifiers } from './recorder/utils';
|
import { toClickOptions, toModifiers } from './recorder/utils';
|
||||||
import { Page } from './page';
|
import type { Page } from './page';
|
||||||
import { Frame } from './frames';
|
import type { Frame } from './frames';
|
||||||
import { BrowserContext } from './browserContext';
|
import type { BrowserContext } from './browserContext';
|
||||||
import { JavaLanguageGenerator } from './recorder/java';
|
import { JavaLanguageGenerator } from './recorder/java';
|
||||||
import { JavaScriptLanguageGenerator } from './recorder/javascript';
|
import { JavaScriptLanguageGenerator } from './recorder/javascript';
|
||||||
import { JsonlLanguageGenerator } from './recorder/jsonl';
|
import { JsonlLanguageGenerator } from './recorder/jsonl';
|
||||||
|
|
@ -38,7 +38,7 @@ import type { Point } from '../common/types';
|
||||||
import type { CallLog, CallLogStatus, EventData, Mode, OverlayState, Source, UIState } from '@recorder/recorderTypes';
|
import type { CallLog, CallLogStatus, EventData, Mode, OverlayState, Source, UIState } from '@recorder/recorderTypes';
|
||||||
import { createGuid, isUnderTest, monotonicTime } from '../utils';
|
import { createGuid, isUnderTest, monotonicTime } from '../utils';
|
||||||
import { metadataToCallLog } from './recorder/recorderUtils';
|
import { metadataToCallLog } from './recorder/recorderUtils';
|
||||||
import { Debugger } from './debugger';
|
import type { Debugger } from './debugger';
|
||||||
import { EventEmitter } from 'events';
|
import { EventEmitter } from 'events';
|
||||||
import { raceAgainstDeadline } from '../utils/timeoutRunner';
|
import { raceAgainstDeadline } from '../utils/timeoutRunner';
|
||||||
import type { Language, LanguageGenerator } from './recorder/language';
|
import type { Language, LanguageGenerator } from './recorder/language';
|
||||||
|
|
@ -156,12 +156,12 @@ export class Recorder implements InstrumentationListener {
|
||||||
this._pushAllSources()
|
this._pushAllSources()
|
||||||
]);
|
]);
|
||||||
|
|
||||||
this._context.once(BrowserContext.Events.Close, () => {
|
this._context.once('close', () => {
|
||||||
this._contextRecorder.dispose();
|
this._contextRecorder.dispose();
|
||||||
this._context.instrumentation.removeListener(this);
|
this._context.instrumentation.removeListener(this);
|
||||||
recorderApp.close().catch(() => {});
|
recorderApp.close().catch(() => {});
|
||||||
});
|
});
|
||||||
this._contextRecorder.on(ContextRecorder.Events.Change, (data: { sources: Source[], primaryFileName: string }) => {
|
this._contextRecorder.on('change', data => {
|
||||||
this._recorderSources = data.sources;
|
this._recorderSources = data.sources;
|
||||||
this._pushAllSources();
|
this._pushAllSources();
|
||||||
this._recorderApp?.setFileIfNeeded(data.primaryFileName);
|
this._recorderApp?.setFileIfNeeded(data.primaryFileName);
|
||||||
|
|
@ -224,7 +224,7 @@ export class Recorder implements InstrumentationListener {
|
||||||
|
|
||||||
if (this._debugger.isPaused())
|
if (this._debugger.isPaused())
|
||||||
this._pausedStateChanged();
|
this._pausedStateChanged();
|
||||||
this._debugger.on(Debugger.Events.PausedStateChanged, () => this._pausedStateChanged());
|
this._debugger.on('pausedstatechanged', () => this._pausedStateChanged());
|
||||||
|
|
||||||
(this._context as any).recorderAppForTest = recorderApp;
|
(this._context as any).recorderAppForTest = recorderApp;
|
||||||
}
|
}
|
||||||
|
|
@ -370,11 +370,9 @@ export class Recorder implements InstrumentationListener {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ContextRecorder extends EventEmitter {
|
class ContextRecorder extends EventEmitter<{
|
||||||
static Events = {
|
change: [{ sources: Source[], primaryFileName: string }]
|
||||||
Change: 'change'
|
}> {
|
||||||
};
|
|
||||||
|
|
||||||
private _generator: CodeGenerator;
|
private _generator: CodeGenerator;
|
||||||
private _pageAliases = new Map<Page, string>();
|
private _pageAliases = new Map<Page, string>();
|
||||||
private _lastPopupOrdinal = 0;
|
private _lastPopupOrdinal = 0;
|
||||||
|
|
@ -417,12 +415,12 @@ class ContextRecorder extends EventEmitter {
|
||||||
if (languageGenerator === this._orderedLanguages[0])
|
if (languageGenerator === this._orderedLanguages[0])
|
||||||
this._throttledOutputFile?.setContent(source.text);
|
this._throttledOutputFile?.setContent(source.text);
|
||||||
}
|
}
|
||||||
this.emit(ContextRecorder.Events.Change, {
|
this.emit('change', {
|
||||||
sources: this._recorderSources,
|
sources: this._recorderSources,
|
||||||
primaryFileName: this._orderedLanguages[0].id
|
primaryFileName: this._orderedLanguages[0].id
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
context.on(BrowserContext.Events.BeforeClose, () => {
|
context.on('beforeclose', () => {
|
||||||
this._throttledOutputFile?.flush();
|
this._throttledOutputFile?.flush();
|
||||||
});
|
});
|
||||||
this._listeners.push(eventsHelper.addEventListener(process, 'exit', () => {
|
this._listeners.push(eventsHelper.addEventListener(process, 'exit', () => {
|
||||||
|
|
@ -463,10 +461,10 @@ class ContextRecorder extends EventEmitter {
|
||||||
}
|
}
|
||||||
|
|
||||||
async install() {
|
async install() {
|
||||||
this._context.on(BrowserContext.Events.Page, (page: Page) => this._onPage(page));
|
this._context.on('page', (page: Page) => this._onPage(page));
|
||||||
for (const page of this._context.pages())
|
for (const page of this._context.pages())
|
||||||
this._onPage(page);
|
this._onPage(page);
|
||||||
this._context.on(BrowserContext.Events.Dialog, (dialog: Dialog) => this._onDialog(dialog.page()));
|
this._context.on('dialog', (dialog: Dialog) => this._onDialog(dialog.page()));
|
||||||
|
|
||||||
// Input actions that potentially lead to navigation are intercepted on the page and are
|
// Input actions that potentially lead to navigation are intercepted on the page and are
|
||||||
// performed by the Playwright.
|
// performed by the Playwright.
|
||||||
|
|
@ -505,11 +503,11 @@ class ContextRecorder extends EventEmitter {
|
||||||
});
|
});
|
||||||
this._pageAliases.delete(page);
|
this._pageAliases.delete(page);
|
||||||
});
|
});
|
||||||
frame.on(Frame.Events.InternalNavigation, event => {
|
frame.on('internalnavigation', event => {
|
||||||
if (event.isPublic)
|
if (event.isPublic)
|
||||||
this._onFrameNavigated(frame, page);
|
this._onFrameNavigated(frame, page);
|
||||||
});
|
});
|
||||||
page.on(Page.Events.Download, () => this._onDownload(page));
|
page.on('download', () => this._onDownload(page));
|
||||||
const suffix = this._pageAliases.size ? String(++this._lastPopupOrdinal) : '';
|
const suffix = this._pageAliases.size ? String(++this._lastPopupOrdinal) : '';
|
||||||
const pageAlias = 'page' + suffix;
|
const pageAlias = 'page' + suffix;
|
||||||
this._pageAliases.set(page, pageAlias);
|
this._pageAliases.set(page, pageAlias);
|
||||||
|
|
|
||||||
|
|
@ -139,7 +139,7 @@ export class ClientCertificatesProxy {
|
||||||
) {
|
) {
|
||||||
this._socksProxy = new SocksProxy();
|
this._socksProxy = new SocksProxy();
|
||||||
this._socksProxy.setPattern('*');
|
this._socksProxy.setPattern('*');
|
||||||
this._socksProxy.addListener(SocksProxy.Events.SocksRequested, async (payload: SocksSocketRequestedPayload) => {
|
this._socksProxy.addListener('socksRequested', async (payload: SocksSocketRequestedPayload) => {
|
||||||
try {
|
try {
|
||||||
const connection = new SocksProxyConnection(this, payload.uid, payload.host, payload.port);
|
const connection = new SocksProxyConnection(this, payload.uid, payload.host, payload.port);
|
||||||
await connection.connect();
|
await connection.connect();
|
||||||
|
|
@ -148,10 +148,10 @@ export class ClientCertificatesProxy {
|
||||||
this._socksProxy.socketFailed({ uid: payload.uid, errorCode: error.code });
|
this._socksProxy.socketFailed({ uid: payload.uid, errorCode: error.code });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
this._socksProxy.addListener(SocksProxy.Events.SocksData, async (payload: SocksSocketDataPayload) => {
|
this._socksProxy.addListener('socksData', async (payload: SocksSocketDataPayload) => {
|
||||||
this._connections.get(payload.uid)?.onData(payload.data);
|
this._connections.get(payload.uid)?.onData(payload.data);
|
||||||
});
|
});
|
||||||
this._socksProxy.addListener(SocksProxy.Events.SocksClosed, (payload: SocksSocketClosedPayload) => {
|
this._socksProxy.addListener('socksClosed', (payload: SocksSocketClosedPayload) => {
|
||||||
this._connections.get(payload.uid)?.onClose();
|
this._connections.get(payload.uid)?.onClose();
|
||||||
this._connections.delete(payload.uid);
|
this._connections.delete(payload.uid);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -47,11 +47,11 @@ export class SocksInterceptor {
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
}) as channels.SocksSupportChannel & EventEmitter;
|
}) as channels.SocksSupportChannel & EventEmitter;
|
||||||
this._handler.on(socks.SocksProxyHandler.Events.SocksConnected, (payload: socks.SocksSocketConnectedPayload) => this._channel.socksConnected(payload));
|
this._handler.on('socksConnected', (payload: socks.SocksSocketConnectedPayload) => this._channel.socksConnected(payload));
|
||||||
this._handler.on(socks.SocksProxyHandler.Events.SocksData, (payload: socks.SocksSocketDataPayload) => this._channel.socksData(payload));
|
this._handler.on('socksData', (payload: socks.SocksSocketDataPayload) => this._channel.socksData(payload));
|
||||||
this._handler.on(socks.SocksProxyHandler.Events.SocksError, (payload: socks.SocksSocketErrorPayload) => this._channel.socksError(payload));
|
this._handler.on('socksError', (payload: socks.SocksSocketErrorPayload) => this._channel.socksError(payload));
|
||||||
this._handler.on(socks.SocksProxyHandler.Events.SocksFailed, (payload: socks.SocksSocketFailedPayload) => this._channel.socksFailed(payload));
|
this._handler.on('socksFailed', (payload: socks.SocksSocketFailedPayload) => this._channel.socksFailed(payload));
|
||||||
this._handler.on(socks.SocksProxyHandler.Events.SocksEnd, (payload: socks.SocksSocketEndPayload) => this._channel.socksEnd(payload));
|
this._handler.on('socksEnd', (payload: socks.SocksSocketEndPayload) => this._channel.socksEnd(payload));
|
||||||
this._channel.on('socksRequested', payload => this._handler.socketRequested(payload));
|
this._channel.on('socksRequested', payload => this._handler.socketRequested(payload));
|
||||||
this._channel.on('socksClosed', payload => this._handler.socketClosed(payload));
|
this._channel.on('socksClosed', payload => this._handler.socketClosed(payload));
|
||||||
this._channel.on('socksData', payload => this._handler.sendSocketData(payload));
|
this._channel.on('socksData', payload => this._handler.sendSocketData(payload));
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,8 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { BrowserContext } from '../../browserContext';
|
import type { BrowserContext } from '../../browserContext';
|
||||||
import { Page } from '../../page';
|
import type { Page } from '../../page';
|
||||||
import type { RegisteredListener } from '../../../utils/eventsHelper';
|
import type { RegisteredListener } from '../../../utils/eventsHelper';
|
||||||
import { eventsHelper } from '../../../utils/eventsHelper';
|
import { eventsHelper } from '../../../utils/eventsHelper';
|
||||||
import { debugLogger } from '../../../utils/debugLogger';
|
import { debugLogger } from '../../../utils/debugLogger';
|
||||||
|
|
@ -83,7 +83,7 @@ export class Snapshotter {
|
||||||
for (const page of this._context.pages())
|
for (const page of this._context.pages())
|
||||||
this._onPage(page);
|
this._onPage(page);
|
||||||
this._eventListeners = [
|
this._eventListeners = [
|
||||||
eventsHelper.addEventListener(this._context, BrowserContext.Events.Page, this._onPage.bind(this)),
|
this._context.addManagedListener('page', this._onPage.bind(this)),
|
||||||
];
|
];
|
||||||
|
|
||||||
const { javaScriptEnabled } = this._context._options;
|
const { javaScriptEnabled } = this._context._options;
|
||||||
|
|
@ -160,7 +160,7 @@ export class Snapshotter {
|
||||||
// Annotate frame hierarchy so that snapshots could include frame ids.
|
// Annotate frame hierarchy so that snapshots could include frame ids.
|
||||||
for (const frame of page.frames())
|
for (const frame of page.frames())
|
||||||
this._annotateFrameHierarchy(frame);
|
this._annotateFrameHierarchy(frame);
|
||||||
this._eventListeners.push(eventsHelper.addEventListener(page, Page.Events.FrameAttached, frame => this._annotateFrameHierarchy(frame)));
|
this._eventListeners.push(page.addManagedListener('frameattached', frame => this._annotateFrameHierarchy(frame)));
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _annotateFrameHierarchy(frame: Frame) {
|
private async _annotateFrameHierarchy(frame: Frame) {
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ import type { ElementHandle } from '../../dom';
|
||||||
import type { APIRequestContext } from '../../fetch';
|
import type { APIRequestContext } from '../../fetch';
|
||||||
import type { CallMetadata, InstrumentationListener } from '../../instrumentation';
|
import type { CallMetadata, InstrumentationListener } from '../../instrumentation';
|
||||||
import { SdkObject } from '../../instrumentation';
|
import { SdkObject } from '../../instrumentation';
|
||||||
import { Page } from '../../page';
|
import type { Page } from '../../page';
|
||||||
import type * as har from '@trace/har';
|
import type * as har from '@trace/har';
|
||||||
import type { HarTracerDelegate } from '../../har/harTracer';
|
import type { HarTracerDelegate } from '../../har/harTracer';
|
||||||
import { HarTracer } from '../../har/harTracer';
|
import { HarTracer } from '../../har/harTracer';
|
||||||
|
|
@ -182,10 +182,12 @@ export class Tracing extends SdkObject implements InstrumentationListener, Snaps
|
||||||
this._fs.appendFile(this._state.traceFile, JSON.stringify(event) + '\n');
|
this._fs.appendFile(this._state.traceFile, JSON.stringify(event) + '\n');
|
||||||
|
|
||||||
this._context.instrumentation.addListener(this, this._context);
|
this._context.instrumentation.addListener(this, this._context);
|
||||||
this._eventListeners.push(
|
if (this._context instanceof BrowserContext) {
|
||||||
eventsHelper.addEventListener(this._context, BrowserContext.Events.Console, this._onConsoleMessage.bind(this)),
|
this._eventListeners.push(
|
||||||
eventsHelper.addEventListener(this._context, BrowserContext.Events.PageError, this._onPageError.bind(this)),
|
this._context.addManagedListener('console', this._onConsoleMessage.bind(this)),
|
||||||
);
|
this._context.addManagedListener('pageerror', this._onPageError.bind(this)),
|
||||||
|
);
|
||||||
|
}
|
||||||
if (this._state.options.screenshots)
|
if (this._state.options.screenshots)
|
||||||
this._startScreencast();
|
this._startScreencast();
|
||||||
if (this._state.options.snapshots)
|
if (this._state.options.snapshots)
|
||||||
|
|
@ -199,7 +201,7 @@ export class Tracing extends SdkObject implements InstrumentationListener, Snaps
|
||||||
for (const page of this._context.pages())
|
for (const page of this._context.pages())
|
||||||
this._startScreencastInPage(page);
|
this._startScreencastInPage(page);
|
||||||
this._screencastListeners.push(
|
this._screencastListeners.push(
|
||||||
eventsHelper.addEventListener(this._context, BrowserContext.Events.Page, this._startScreencastInPage.bind(this)),
|
this._context.addManagedListener('page', this._startScreencastInPage.bind(this)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -463,7 +465,7 @@ export class Tracing extends SdkObject implements InstrumentationListener, Snaps
|
||||||
page.setScreencastOptions(kScreencastOptions);
|
page.setScreencastOptions(kScreencastOptions);
|
||||||
const prefix = page.guid;
|
const prefix = page.guid;
|
||||||
this._screencastListeners.push(
|
this._screencastListeners.push(
|
||||||
eventsHelper.addEventListener(page, Page.Events.ScreencastFrame, params => {
|
page.addManagedListener('screencastframe', params => {
|
||||||
const suffix = params.timestamp || Date.now();
|
const suffix = params.timestamp || Date.now();
|
||||||
const sha1 = `${prefix}-${suffix}.jpeg`;
|
const sha1 = `${prefix}-${suffix}.jpeg`;
|
||||||
const event: trace.ScreencastFrameTraceEvent = {
|
const event: trace.ScreencastFrameTraceEvent = {
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,6 @@ import { Browser } from '../browser';
|
||||||
import { assertBrowserContextIsNotOwned, BrowserContext, verifyGeolocation } from '../browserContext';
|
import { assertBrowserContextIsNotOwned, BrowserContext, verifyGeolocation } from '../browserContext';
|
||||||
import type { RegisteredListener } from '../../utils/eventsHelper';
|
import type { RegisteredListener } from '../../utils/eventsHelper';
|
||||||
import { assert } from '../../utils';
|
import { assert } from '../../utils';
|
||||||
import { eventsHelper } from '../../utils/eventsHelper';
|
|
||||||
import * as network from '../network';
|
import * as network from '../network';
|
||||||
import type { InitScript, Page, PageBinding, PageDelegate } from '../page';
|
import type { InitScript, Page, PageBinding, PageDelegate } from '../page';
|
||||||
import type { ConnectionTransport } from '../transport';
|
import type { ConnectionTransport } from '../transport';
|
||||||
|
|
@ -64,15 +63,15 @@ export class WKBrowser extends Browser {
|
||||||
this._connection = new WKConnection(transport, this._onDisconnect.bind(this), options.protocolLogger, options.browserLogsCollector);
|
this._connection = new WKConnection(transport, this._onDisconnect.bind(this), options.protocolLogger, options.browserLogsCollector);
|
||||||
this._browserSession = this._connection.browserSession;
|
this._browserSession = this._connection.browserSession;
|
||||||
this._eventListeners = [
|
this._eventListeners = [
|
||||||
eventsHelper.addEventListener(this._browserSession, 'Playwright.pageProxyCreated', this._onPageProxyCreated.bind(this)),
|
this._browserSession.addManagedListener('Playwright.pageProxyCreated', this._onPageProxyCreated.bind(this)),
|
||||||
eventsHelper.addEventListener(this._browserSession, 'Playwright.pageProxyDestroyed', this._onPageProxyDestroyed.bind(this)),
|
this._browserSession.addManagedListener('Playwright.pageProxyDestroyed', this._onPageProxyDestroyed.bind(this)),
|
||||||
eventsHelper.addEventListener(this._browserSession, 'Playwright.provisionalLoadFailed', event => this._onProvisionalLoadFailed(event)),
|
this._browserSession.addManagedListener('Playwright.provisionalLoadFailed', event => this._onProvisionalLoadFailed(event)),
|
||||||
eventsHelper.addEventListener(this._browserSession, 'Playwright.windowOpen', event => this._onWindowOpen(event)),
|
this._browserSession.addManagedListener('Playwright.windowOpen', event => this._onWindowOpen(event)),
|
||||||
eventsHelper.addEventListener(this._browserSession, 'Playwright.downloadCreated', this._onDownloadCreated.bind(this)),
|
this._browserSession.addManagedListener('Playwright.downloadCreated', this._onDownloadCreated.bind(this)),
|
||||||
eventsHelper.addEventListener(this._browserSession, 'Playwright.downloadFilenameSuggested', this._onDownloadFilenameSuggested.bind(this)),
|
this._browserSession.addManagedListener('Playwright.downloadFilenameSuggested', this._onDownloadFilenameSuggested.bind(this)),
|
||||||
eventsHelper.addEventListener(this._browserSession, 'Playwright.downloadFinished', this._onDownloadFinished.bind(this)),
|
this._browserSession.addManagedListener('Playwright.downloadFinished', this._onDownloadFinished.bind(this)),
|
||||||
eventsHelper.addEventListener(this._browserSession, 'Playwright.screencastFinished', this._onScreencastFinished.bind(this)),
|
this._browserSession.addManagedListener('Playwright.screencastFinished', this._onScreencastFinished.bind(this)),
|
||||||
eventsHelper.addEventListener(this._browserSession, kPageProxyMessageReceived, this._onPageProxyMessageReceived.bind(this)),
|
this._browserSession.addManagedListener(kPageProxyMessageReceived, this._onPageProxyMessageReceived.bind(this)),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,8 +15,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { EventEmitter } from 'events';
|
import { assert, ManagedEventEmitter } from '../../utils';
|
||||||
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 type { RecentLogsCollector } from '../../utils/debugLogger';
|
import type { RecentLogsCollector } from '../../utils/debugLogger';
|
||||||
|
|
@ -97,7 +96,11 @@ export class WKConnection {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class WKSession extends EventEmitter {
|
export class WKSession extends ManagedEventEmitter<{
|
||||||
|
[K in keyof Protocol.Events]: [Protocol.Events[K]]
|
||||||
|
} & {
|
||||||
|
[kPageProxyMessageReceived]: [PageProxyMessageReceivedPayload]
|
||||||
|
}> {
|
||||||
connection: WKConnection;
|
connection: WKConnection;
|
||||||
readonly sessionId: string;
|
readonly sessionId: string;
|
||||||
|
|
||||||
|
|
@ -106,12 +109,6 @@ export class WKSession extends EventEmitter {
|
||||||
private readonly _callbacks = new Map<number, { resolve: (o: any) => void, reject: (e: ProtocolError) => void, error: ProtocolError }>();
|
private readonly _callbacks = new Map<number, { resolve: (o: any) => void, reject: (e: ProtocolError) => void, error: ProtocolError }>();
|
||||||
private _crashed: boolean = false;
|
private _crashed: boolean = false;
|
||||||
|
|
||||||
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(connection: WKConnection, sessionId: string, rawSend: (message: any) => void) {
|
constructor(connection: WKConnection, sessionId: string, rawSend: (message: any) => void) {
|
||||||
super();
|
super();
|
||||||
this.setMaxListeners(0);
|
this.setMaxListeners(0);
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,6 @@ import { WKProvisionalPage } from './wkProvisionalPage';
|
||||||
import { WKWorkers } from './wkWorkers';
|
import { WKWorkers } from './wkWorkers';
|
||||||
import { debugLogger } from '../../utils/debugLogger';
|
import { debugLogger } from '../../utils/debugLogger';
|
||||||
import { ManualPromise } from '../../utils/manualPromise';
|
import { ManualPromise } from '../../utils/manualPromise';
|
||||||
import { BrowserContext } from '../browserContext';
|
|
||||||
import { TargetClosedError } from '../errors';
|
import { TargetClosedError } from '../errors';
|
||||||
|
|
||||||
const UTILITY_WORLD_NAME = '__playwright_utility_world__';
|
const UTILITY_WORLD_NAME = '__playwright_utility_world__';
|
||||||
|
|
@ -92,13 +91,13 @@ export class WKPage implements PageDelegate {
|
||||||
this._workers = new WKWorkers(this._page);
|
this._workers = new WKWorkers(this._page);
|
||||||
this._session = undefined as any as WKSession;
|
this._session = undefined as any as WKSession;
|
||||||
this._browserContext = browserContext;
|
this._browserContext = browserContext;
|
||||||
this._page.on(Page.Events.FrameDetached, (frame: frames.Frame) => this._removeContextsForFrame(frame, false));
|
this._page.on('framedetached', (frame: frames.Frame) => this._removeContextsForFrame(frame, false));
|
||||||
this._eventListeners = [
|
this._eventListeners = [
|
||||||
eventsHelper.addEventListener(this._pageProxySession, 'Target.targetCreated', this._onTargetCreated.bind(this)),
|
this._pageProxySession.addManagedListener('Target.targetCreated', this._onTargetCreated.bind(this)),
|
||||||
eventsHelper.addEventListener(this._pageProxySession, 'Target.targetDestroyed', this._onTargetDestroyed.bind(this)),
|
this._pageProxySession.addManagedListener('Target.targetDestroyed', this._onTargetDestroyed.bind(this)),
|
||||||
eventsHelper.addEventListener(this._pageProxySession, 'Target.dispatchMessageFromTarget', this._onDispatchMessageFromTarget.bind(this)),
|
this._pageProxySession.addManagedListener('Target.dispatchMessageFromTarget', this._onDispatchMessageFromTarget.bind(this)),
|
||||||
eventsHelper.addEventListener(this._pageProxySession, 'Target.didCommitProvisionalTarget', this._onDidCommitProvisionalTarget.bind(this)),
|
this._pageProxySession.addManagedListener('Target.didCommitProvisionalTarget', this._onDidCommitProvisionalTarget.bind(this)),
|
||||||
eventsHelper.addEventListener(this._pageProxySession, 'Screencast.screencastFrame', this._onScreencastFrame.bind(this)),
|
this._pageProxySession.addManagedListener('Screencast.screencastFrame', this._onScreencastFrame.bind(this)),
|
||||||
];
|
];
|
||||||
this._firstNonInitialNavigationCommittedPromise = new Promise((f, r) => {
|
this._firstNonInitialNavigationCommittedPromise = new Promise((f, r) => {
|
||||||
this._firstNonInitialNavigationCommittedFulfill = f;
|
this._firstNonInitialNavigationCommittedFulfill = f;
|
||||||
|
|
@ -373,33 +372,33 @@ export class WKPage implements PageDelegate {
|
||||||
|
|
||||||
private _addSessionListeners() {
|
private _addSessionListeners() {
|
||||||
this._sessionListeners = [
|
this._sessionListeners = [
|
||||||
eventsHelper.addEventListener(this._session, 'Page.frameNavigated', event => this._onFrameNavigated(event.frame, false)),
|
this._session.addManagedListener('Page.frameNavigated', event => this._onFrameNavigated(event.frame, false)),
|
||||||
eventsHelper.addEventListener(this._session, 'Page.navigatedWithinDocument', event => this._onFrameNavigatedWithinDocument(event.frameId, event.url)),
|
this._session.addManagedListener('Page.navigatedWithinDocument', event => this._onFrameNavigatedWithinDocument(event.frameId, event.url)),
|
||||||
eventsHelper.addEventListener(this._session, 'Page.frameAttached', event => this._onFrameAttached(event.frameId, event.parentFrameId)),
|
this._session.addManagedListener('Page.frameAttached', event => this._onFrameAttached(event.frameId, event.parentFrameId)),
|
||||||
eventsHelper.addEventListener(this._session, 'Page.frameDetached', event => this._onFrameDetached(event.frameId)),
|
this._session.addManagedListener('Page.frameDetached', event => this._onFrameDetached(event.frameId)),
|
||||||
eventsHelper.addEventListener(this._session, 'Page.willCheckNavigationPolicy', event => this._onWillCheckNavigationPolicy(event.frameId)),
|
this._session.addManagedListener('Page.willCheckNavigationPolicy', event => this._onWillCheckNavigationPolicy(event.frameId)),
|
||||||
eventsHelper.addEventListener(this._session, 'Page.didCheckNavigationPolicy', event => this._onDidCheckNavigationPolicy(event.frameId, event.cancel)),
|
this._session.addManagedListener('Page.didCheckNavigationPolicy', event => this._onDidCheckNavigationPolicy(event.frameId, event.cancel)),
|
||||||
eventsHelper.addEventListener(this._session, 'Page.frameScheduledNavigation', event => this._onFrameScheduledNavigation(event.frameId, event.delay, event.targetIsCurrentFrame)),
|
this._session.addManagedListener('Page.frameScheduledNavigation', event => this._onFrameScheduledNavigation(event.frameId, event.delay, event.targetIsCurrentFrame)),
|
||||||
eventsHelper.addEventListener(this._session, 'Page.loadEventFired', event => this._page._frameManager.frameLifecycleEvent(event.frameId, 'load')),
|
this._session.addManagedListener('Page.loadEventFired', event => this._page._frameManager.frameLifecycleEvent(event.frameId, 'load')),
|
||||||
eventsHelper.addEventListener(this._session, 'Page.domContentEventFired', event => this._page._frameManager.frameLifecycleEvent(event.frameId, 'domcontentloaded')),
|
this._session.addManagedListener('Page.domContentEventFired', event => this._page._frameManager.frameLifecycleEvent(event.frameId, 'domcontentloaded')),
|
||||||
eventsHelper.addEventListener(this._session, 'Runtime.executionContextCreated', event => this._onExecutionContextCreated(event.context)),
|
this._session.addManagedListener('Runtime.executionContextCreated', event => this._onExecutionContextCreated(event.context)),
|
||||||
eventsHelper.addEventListener(this._session, 'Runtime.bindingCalled', event => this._onBindingCalled(event.contextId, event.argument)),
|
this._session.addManagedListener('Runtime.bindingCalled', event => this._onBindingCalled(event.contextId, event.argument)),
|
||||||
eventsHelper.addEventListener(this._session, 'Console.messageAdded', event => this._onConsoleMessage(event)),
|
this._session.addManagedListener('Console.messageAdded', event => this._onConsoleMessage(event)),
|
||||||
eventsHelper.addEventListener(this._session, 'Console.messageRepeatCountUpdated', event => this._onConsoleRepeatCountUpdated(event)),
|
this._session.addManagedListener('Console.messageRepeatCountUpdated', event => this._onConsoleRepeatCountUpdated(event)),
|
||||||
eventsHelper.addEventListener(this._pageProxySession, 'Dialog.javascriptDialogOpening', event => this._onDialog(event)),
|
this._pageProxySession.addManagedListener('Dialog.javascriptDialogOpening', event => this._onDialog(event)),
|
||||||
eventsHelper.addEventListener(this._session, 'Page.fileChooserOpened', event => this._onFileChooserOpened(event)),
|
this._session.addManagedListener('Page.fileChooserOpened', event => this._onFileChooserOpened(event)),
|
||||||
eventsHelper.addEventListener(this._session, 'Network.requestWillBeSent', e => this._onRequestWillBeSent(this._session, e)),
|
this._session.addManagedListener('Network.requestWillBeSent', e => this._onRequestWillBeSent(this._session, e)),
|
||||||
eventsHelper.addEventListener(this._session, 'Network.requestIntercepted', e => this._onRequestIntercepted(this._session, e)),
|
this._session.addManagedListener('Network.requestIntercepted', e => this._onRequestIntercepted(this._session, e)),
|
||||||
eventsHelper.addEventListener(this._session, 'Network.responseReceived', e => this._onResponseReceived(this._session, e)),
|
this._session.addManagedListener('Network.responseReceived', e => this._onResponseReceived(this._session, e)),
|
||||||
eventsHelper.addEventListener(this._session, 'Network.loadingFinished', e => this._onLoadingFinished(e)),
|
this._session.addManagedListener('Network.loadingFinished', e => this._onLoadingFinished(e)),
|
||||||
eventsHelper.addEventListener(this._session, 'Network.loadingFailed', e => this._onLoadingFailed(this._session, e)),
|
this._session.addManagedListener('Network.loadingFailed', e => this._onLoadingFailed(this._session, e)),
|
||||||
eventsHelper.addEventListener(this._session, 'Network.webSocketCreated', e => this._page._frameManager.onWebSocketCreated(e.requestId, e.url)),
|
this._session.addManagedListener('Network.webSocketCreated', e => this._page._frameManager.onWebSocketCreated(e.requestId, e.url)),
|
||||||
eventsHelper.addEventListener(this._session, 'Network.webSocketWillSendHandshakeRequest', e => this._page._frameManager.onWebSocketRequest(e.requestId)),
|
this._session.addManagedListener('Network.webSocketWillSendHandshakeRequest', e => this._page._frameManager.onWebSocketRequest(e.requestId)),
|
||||||
eventsHelper.addEventListener(this._session, 'Network.webSocketHandshakeResponseReceived', e => this._page._frameManager.onWebSocketResponse(e.requestId, e.response.status, e.response.statusText)),
|
this._session.addManagedListener('Network.webSocketHandshakeResponseReceived', e => this._page._frameManager.onWebSocketResponse(e.requestId, e.response.status, e.response.statusText)),
|
||||||
eventsHelper.addEventListener(this._session, 'Network.webSocketFrameSent', e => e.response.payloadData && this._page._frameManager.onWebSocketFrameSent(e.requestId, e.response.opcode, e.response.payloadData)),
|
this._session.addManagedListener('Network.webSocketFrameSent', e => e.response.payloadData && this._page._frameManager.onWebSocketFrameSent(e.requestId, e.response.opcode, e.response.payloadData)),
|
||||||
eventsHelper.addEventListener(this._session, 'Network.webSocketFrameReceived', e => e.response.payloadData && this._page._frameManager.webSocketFrameReceived(e.requestId, e.response.opcode, e.response.payloadData)),
|
this._session.addManagedListener('Network.webSocketFrameReceived', e => e.response.payloadData && this._page._frameManager.webSocketFrameReceived(e.requestId, e.response.opcode, e.response.payloadData)),
|
||||||
eventsHelper.addEventListener(this._session, 'Network.webSocketClosed', e => this._page._frameManager.webSocketClosed(e.requestId)),
|
this._session.addManagedListener('Network.webSocketClosed', e => this._page._frameManager.webSocketClosed(e.requestId)),
|
||||||
eventsHelper.addEventListener(this._session, 'Network.webSocketFrameError', e => this._page._frameManager.webSocketError(e.requestId, e.errorMessage)),
|
this._session.addManagedListener('Network.webSocketFrameError', e => this._page._frameManager.webSocketError(e.requestId, e.errorMessage)),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
private async _updateState<T extends keyof Protocol.CommandParameters>(
|
private async _updateState<T extends keyof Protocol.CommandParameters>(
|
||||||
|
|
@ -451,7 +450,7 @@ export class WKPage implements PageDelegate {
|
||||||
}
|
}
|
||||||
|
|
||||||
private _handleFrameTree(frameTree: Protocol.Page.FrameResourceTree) {
|
private _handleFrameTree(frameTree: Protocol.Page.FrameResourceTree) {
|
||||||
this._onFrameAttached(frameTree.frame.id, frameTree.frame.parentId || null);
|
this._onFrameAttached(frameTree.frame.id, frameTree.frame.parentId);
|
||||||
this._onFrameNavigated(frameTree.frame, true);
|
this._onFrameNavigated(frameTree.frame, true);
|
||||||
this._page._frameManager.frameLifecycleEvent(frameTree.frame.id, 'domcontentloaded');
|
this._page._frameManager.frameLifecycleEvent(frameTree.frame.id, 'domcontentloaded');
|
||||||
this._page._frameManager.frameLifecycleEvent(frameTree.frame.id, 'load');
|
this._page._frameManager.frameLifecycleEvent(frameTree.frame.id, 'load');
|
||||||
|
|
@ -462,7 +461,7 @@ export class WKPage implements PageDelegate {
|
||||||
this._handleFrameTree(child);
|
this._handleFrameTree(child);
|
||||||
}
|
}
|
||||||
|
|
||||||
_onFrameAttached(frameId: string, parentFrameId: string | null): frames.Frame {
|
_onFrameAttached(frameId: string, parentFrameId: string | undefined): frames.Frame {
|
||||||
return this._page._frameManager.frameAttached(frameId, parentFrameId);
|
return this._page._frameManager.frameAttached(frameId, parentFrameId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -552,7 +551,7 @@ export class WKPage implements PageDelegate {
|
||||||
error.stack = stack;
|
error.stack = stack;
|
||||||
error.name = name;
|
error.name = name;
|
||||||
|
|
||||||
this._page.emitOnContextOnceInitialized(BrowserContext.Events.PageError, error, this._page);
|
this._page.emitOnContextOnceInitialized('pageerror', error, this._page);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -606,7 +605,7 @@ export class WKPage implements PageDelegate {
|
||||||
}
|
}
|
||||||
|
|
||||||
_onDialog(event: Protocol.Dialog.javascriptDialogOpeningPayload) {
|
_onDialog(event: Protocol.Dialog.javascriptDialogOpeningPayload) {
|
||||||
this._page.emitOnContext(BrowserContext.Events.Dialog, new dialog.Dialog(
|
this._page.emitOnContext('dialog', new dialog.Dialog(
|
||||||
this._page,
|
this._page,
|
||||||
event.type as dialog.DialogType,
|
event.type as dialog.DialogType,
|
||||||
event.message,
|
event.message,
|
||||||
|
|
@ -937,7 +936,7 @@ export class WKPage implements PageDelegate {
|
||||||
this._pageProxySession.send('Screencast.screencastFrameAck', { generation }).catch(e => debugLogger.log('error', e));
|
this._pageProxySession.send('Screencast.screencastFrameAck', { generation }).catch(e => debugLogger.log('error', e));
|
||||||
});
|
});
|
||||||
const buffer = Buffer.from(event.data, 'base64');
|
const buffer = Buffer.from(event.data, 'base64');
|
||||||
this._page.emit(Page.Events.ScreencastFrame, {
|
this._page.emit('screencastframe', {
|
||||||
buffer,
|
buffer,
|
||||||
width: event.deviceWidth,
|
width: event.deviceWidth,
|
||||||
height: event.deviceHeight,
|
height: event.deviceHeight,
|
||||||
|
|
|
||||||
|
|
@ -43,11 +43,11 @@ export class WKProvisionalPage {
|
||||||
const wkPage = this._wkPage;
|
const wkPage = this._wkPage;
|
||||||
|
|
||||||
this._sessionListeners = [
|
this._sessionListeners = [
|
||||||
eventsHelper.addEventListener(session, 'Network.requestWillBeSent', overrideFrameId(e => wkPage._onRequestWillBeSent(session, e))),
|
session.addManagedListener('Network.requestWillBeSent', overrideFrameId(e => wkPage._onRequestWillBeSent(session, e))),
|
||||||
eventsHelper.addEventListener(session, 'Network.requestIntercepted', overrideFrameId(e => wkPage._onRequestIntercepted(session, e))),
|
session.addManagedListener('Network.requestIntercepted', overrideFrameId(e => wkPage._onRequestIntercepted(session, e))),
|
||||||
eventsHelper.addEventListener(session, 'Network.responseReceived', overrideFrameId(e => wkPage._onResponseReceived(session, e))),
|
session.addManagedListener('Network.responseReceived', overrideFrameId(e => wkPage._onResponseReceived(session, e))),
|
||||||
eventsHelper.addEventListener(session, 'Network.loadingFinished', overrideFrameId(e => wkPage._onLoadingFinished(e))),
|
session.addManagedListener('Network.loadingFinished', overrideFrameId(e => wkPage._onLoadingFinished(e))),
|
||||||
eventsHelper.addEventListener(session, 'Network.loadingFailed', overrideFrameId(e => wkPage._onLoadingFailed(session, e))),
|
session.addManagedListener('Network.loadingFailed', overrideFrameId(e => wkPage._onLoadingFailed(session, e))),
|
||||||
];
|
];
|
||||||
|
|
||||||
this.initializationPromise = this._wkPage._initializeSession(session, true, ({ frameTree }) => this._handleFrameTree(frameTree));
|
this.initializationPromise = this._wkPage._initializeSession(session, true, ({ frameTree }) => this._handleFrameTree(frameTree));
|
||||||
|
|
@ -59,7 +59,7 @@ export class WKProvisionalPage {
|
||||||
|
|
||||||
commit() {
|
commit() {
|
||||||
assert(this._mainFrameId);
|
assert(this._mainFrameId);
|
||||||
this._wkPage._onFrameAttached(this._mainFrameId, null);
|
this._wkPage._onFrameAttached(this._mainFrameId, undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _handleFrameTree(frameTree: Protocol.Page.FrameResourceTree) {
|
private _handleFrameTree(frameTree: Protocol.Page.FrameResourceTree) {
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ export class WKWorkers {
|
||||||
eventsHelper.removeEventListeners(this._sessionListeners);
|
eventsHelper.removeEventListeners(this._sessionListeners);
|
||||||
this.clear();
|
this.clear();
|
||||||
this._sessionListeners = [
|
this._sessionListeners = [
|
||||||
eventsHelper.addEventListener(session, 'Worker.workerCreated', (event: Protocol.Worker.workerCreatedPayload) => {
|
session.addManagedListener('Worker.workerCreated', (event: Protocol.Worker.workerCreatedPayload) => {
|
||||||
const worker = new Worker(this._page, event.url);
|
const worker = new Worker(this._page, event.url);
|
||||||
const workerSession = new WKSession(session.connection, event.workerId, (message: any) => {
|
const workerSession = new WKSession(session.connection, event.workerId, (message: any) => {
|
||||||
session.send('Worker.sendMessageToWorker', {
|
session.send('Worker.sendMessageToWorker', {
|
||||||
|
|
@ -59,13 +59,13 @@ export class WKWorkers {
|
||||||
this._page._removeWorker(event.workerId);
|
this._page._removeWorker(event.workerId);
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
eventsHelper.addEventListener(session, 'Worker.dispatchMessageFromWorker', (event: Protocol.Worker.dispatchMessageFromWorkerPayload) => {
|
session.addManagedListener('Worker.dispatchMessageFromWorker', (event: Protocol.Worker.dispatchMessageFromWorkerPayload) => {
|
||||||
const workerSession = this._workerSessions.get(event.workerId)!;
|
const workerSession = this._workerSessions.get(event.workerId)!;
|
||||||
if (!workerSession)
|
if (!workerSession)
|
||||||
return;
|
return;
|
||||||
workerSession.dispatchMessage(JSON.parse(event.message));
|
workerSession.dispatchMessage(JSON.parse(event.message));
|
||||||
}),
|
}),
|
||||||
eventsHelper.addEventListener(session, 'Worker.workerTerminated', (event: Protocol.Worker.workerTerminatedPayload) => {
|
session.addManagedListener('Worker.workerTerminated', (event: Protocol.Worker.workerTerminatedPayload) => {
|
||||||
const workerSession = this._workerSessions.get(event.workerId)!;
|
const workerSession = this._workerSessions.get(event.workerId)!;
|
||||||
if (!workerSession)
|
if (!workerSession)
|
||||||
return;
|
return;
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { EventEmitter } from 'events';
|
import { EventEmitter } from 'events';
|
||||||
|
|
||||||
export type RegisteredListener = {
|
export type RegisteredListener = {
|
||||||
emitter: EventEmitter;
|
emitter: EventEmitter;
|
||||||
|
|
@ -24,6 +24,8 @@ export type RegisteredListener = {
|
||||||
};
|
};
|
||||||
|
|
||||||
class EventsHelper {
|
class EventsHelper {
|
||||||
|
// Used for normal EventEmitter instances, in order to benefit from
|
||||||
|
// auto-completion, use ManagedEventEmitter.addManagedListener instead
|
||||||
static addEventListener(
|
static addEventListener(
|
||||||
emitter: EventEmitter,
|
emitter: EventEmitter,
|
||||||
eventName: (string | symbol),
|
eventName: (string | symbol),
|
||||||
|
|
@ -43,4 +45,28 @@ class EventsHelper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// From node_modules/@types/node/events.d.ts
|
||||||
|
type DefaultEventMap = [never];
|
||||||
|
type Listener<K, T, F> = T extends DefaultEventMap ? F : (
|
||||||
|
K extends keyof T ? (
|
||||||
|
T[K] extends unknown[] ? (...args: T[K]) => void : never
|
||||||
|
)
|
||||||
|
: never
|
||||||
|
);
|
||||||
|
type Listener1<K, T> = Listener<K, T, (...args: any[]) => void>;
|
||||||
|
type Key<K, T> = T extends DefaultEventMap ? string | symbol : K | keyof T;
|
||||||
|
|
||||||
|
export class ManagedEventEmitter<T extends Record<keyof T, any[]>> extends EventEmitter<T> {
|
||||||
|
public _events!: T;
|
||||||
|
|
||||||
|
addManagedListener<K>(eventName: Key<K, T>, listener: Listener1<K, T>): RegisteredListener {
|
||||||
|
this.on(eventName, listener);
|
||||||
|
return {
|
||||||
|
emitter: this as EventEmitter,
|
||||||
|
eventName: eventName as string,
|
||||||
|
handler: listener
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export const eventsHelper = EventsHelper;
|
export const eventsHelper = EventsHelper;
|
||||||
|
|
|
||||||
|
|
@ -504,13 +504,13 @@ class JobDispatcher {
|
||||||
worker.runTestGroup(runPayload);
|
worker.runTestGroup(runPayload);
|
||||||
|
|
||||||
this._listeners = [
|
this._listeners = [
|
||||||
eventsHelper.addEventListener(worker, 'testBegin', this._onTestBegin.bind(this)),
|
worker.addManagedListener('testBegin', this._onTestBegin.bind(this)),
|
||||||
eventsHelper.addEventListener(worker, 'testEnd', this._onTestEnd.bind(this)),
|
worker.addManagedListener('testEnd', this._onTestEnd.bind(this)),
|
||||||
eventsHelper.addEventListener(worker, 'stepBegin', this._onStepBegin.bind(this)),
|
worker.addManagedListener('stepBegin', this._onStepBegin.bind(this)),
|
||||||
eventsHelper.addEventListener(worker, 'stepEnd', this._onStepEnd.bind(this)),
|
worker.addManagedListener('stepEnd', this._onStepEnd.bind(this)),
|
||||||
eventsHelper.addEventListener(worker, 'attach', this._onAttach.bind(this)),
|
worker.addManagedListener('attach', this._onAttach.bind(this)),
|
||||||
eventsHelper.addEventListener(worker, 'done', this._onDone.bind(this)),
|
worker.addManagedListener('done', this._onDone.bind(this)),
|
||||||
eventsHelper.addEventListener(worker, 'exit', this.onExit.bind(this)),
|
worker.addManagedListener('exit', this.onExit.bind(this)),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,13 +15,13 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import child_process from 'child_process';
|
import child_process from 'child_process';
|
||||||
import { EventEmitter } from 'events';
|
|
||||||
import { debug } from 'playwright-core/lib/utilsBundle';
|
import { debug } from 'playwright-core/lib/utilsBundle';
|
||||||
import type { EnvProducedPayload, ProcessInitParams } from '../common/ipc';
|
import type { EnvProducedPayload, ProcessInitParams } from '../common/ipc';
|
||||||
import type { ProtocolResponse } from '../common/process';
|
import type { ProtocolResponse } from '../common/process';
|
||||||
import { execArgvWithExperimentalLoaderOptions } from '../transform/esmUtils';
|
import { execArgvWithExperimentalLoaderOptions } from '../transform/esmUtils';
|
||||||
import { assert } from 'playwright-core/lib/utils';
|
import { assert, ManagedEventEmitter } from 'playwright-core/lib/utils';
|
||||||
import { esmLoaderRegistered } from '../common/esmLoaderHost';
|
import { esmLoaderRegistered } from '../common/esmLoaderHost';
|
||||||
|
import type { WorkerHostEvents } from './workerHost';
|
||||||
|
|
||||||
export type ProcessExitData = {
|
export type ProcessExitData = {
|
||||||
unexpectedly: boolean;
|
unexpectedly: boolean;
|
||||||
|
|
@ -29,7 +29,7 @@ export type ProcessExitData = {
|
||||||
signal: NodeJS.Signals | null;
|
signal: NodeJS.Signals | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
export class ProcessHost extends EventEmitter {
|
export class ProcessHost extends ManagedEventEmitter<WorkerHostEvents> {
|
||||||
private process: child_process.ChildProcess | undefined;
|
private process: child_process.ChildProcess | undefined;
|
||||||
private _didSendStop = false;
|
private _didSendStop = false;
|
||||||
private _processDidExit = false;
|
private _processDidExit = false;
|
||||||
|
|
@ -69,7 +69,7 @@ export class ProcessHost extends EventEmitter {
|
||||||
this._processDidExit = true;
|
this._processDidExit = true;
|
||||||
await this.onExit();
|
await this.onExit();
|
||||||
this._didExitAndRanOnExit = true;
|
this._didExitAndRanOnExit = true;
|
||||||
this.emit('exit', { unexpectedly: !this._didSendStop, code, signal } as ProcessExitData);
|
this.emit('exit', { unexpectedly: !this._didSendStop, code, signal });
|
||||||
});
|
});
|
||||||
this.process.on('error', e => {}); // do not yell at a send to dead process.
|
this.process.on('error', e => {}); // do not yell at a send to dead process.
|
||||||
this.process.on('message', (message: any) => {
|
this.process.on('message', (message: any) => {
|
||||||
|
|
@ -91,6 +91,7 @@ export class ProcessHost extends EventEmitter {
|
||||||
resolve(result);
|
resolve(result);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
// @ts-ignore
|
||||||
this.emit(method!, params);
|
this.emit(method!, params);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -148,7 +149,7 @@ export class ProcessHost extends EventEmitter {
|
||||||
this._didSendStop = true;
|
this._didSendStop = true;
|
||||||
}
|
}
|
||||||
if (!this._didExitAndRanOnExit)
|
if (!this._didExitAndRanOnExit)
|
||||||
await new Promise(f => this.once('exit', f));
|
await new Promise<any>(f => this.once('exit', f));
|
||||||
}
|
}
|
||||||
|
|
||||||
didSendStop() {
|
didSendStop() {
|
||||||
|
|
|
||||||
|
|
@ -18,13 +18,29 @@ import fs from 'fs';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import type { TestGroup } from './testGroups';
|
import type { TestGroup } from './testGroups';
|
||||||
import { stdioChunkToParams } from '../common/ipc';
|
import { stdioChunkToParams } from '../common/ipc';
|
||||||
import type { RunPayload, SerializedConfig, WorkerInitParams } from '../common/ipc';
|
import type { AttachmentPayload, DonePayload, RunPayload, SerializedConfig, StepBeginPayload, StepEndPayload, TeardownErrorsPayload, TestBeginPayload, TestEndPayload, TestOutputPayload, WorkerInitParams } from '../common/ipc';
|
||||||
|
import type { ProcessExitData } from './processHost';
|
||||||
import { ProcessHost } from './processHost';
|
import { ProcessHost } from './processHost';
|
||||||
import { artifactsFolderName } from '../isomorphic/folders';
|
import { artifactsFolderName } from '../isomorphic/folders';
|
||||||
import { removeFolders } from 'playwright-core/lib/utils';
|
import { removeFolders } from 'playwright-core/lib/utils';
|
||||||
|
|
||||||
let lastWorkerIndex = 0;
|
let lastWorkerIndex = 0;
|
||||||
|
|
||||||
|
export type WorkerHostEvents = {
|
||||||
|
stdOut: [TestOutputPayload]
|
||||||
|
stdErr: [TestOutputPayload]
|
||||||
|
testBegin: [TestBeginPayload]
|
||||||
|
testEnd: [TestEndPayload]
|
||||||
|
stepBegin: [StepBeginPayload]
|
||||||
|
stepEnd: [StepEndPayload]
|
||||||
|
attach: [AttachmentPayload]
|
||||||
|
done: [DonePayload]
|
||||||
|
teardownErrors: [TeardownErrorsPayload]
|
||||||
|
|
||||||
|
exit: [ProcessExitData]
|
||||||
|
ready: []
|
||||||
|
};
|
||||||
|
|
||||||
export class WorkerHost extends ProcessHost {
|
export class WorkerHost extends ProcessHost {
|
||||||
readonly parallelIndex: number;
|
readonly parallelIndex: number;
|
||||||
readonly workerIndex: number;
|
readonly workerIndex: number;
|
||||||
|
|
|
||||||
|
|
@ -18,9 +18,9 @@ import type { Fixtures } from '@playwright/test';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import { TestServer } from './testserver';
|
import { TestServer } from './testserver';
|
||||||
import { TestProxy } from './proxy';
|
import { TestProxy } from './proxy';
|
||||||
import type { SocksSocketRequestedPayload } from '../../packages/playwright-core/src/common/socksProxy';
|
|
||||||
|
|
||||||
import { SocksProxy } from '../../packages/playwright-core/lib/common/socksProxy';
|
import type { SocksProxy } from '../../packages/playwright-core/src/common/socksProxy';
|
||||||
|
const SocksProxyImpl = require('../../packages/playwright-core/lib/common/socksProxy').SocksProxy as any as typeof SocksProxy;
|
||||||
|
|
||||||
export type ServerWorkerOptions = {
|
export type ServerWorkerOptions = {
|
||||||
loopback?: string;
|
loopback?: string;
|
||||||
|
|
@ -100,16 +100,16 @@ export class MockSocksServer {
|
||||||
private _socksProxy: SocksProxy;
|
private _socksProxy: SocksProxy;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this._socksProxy = new SocksProxy();
|
this._socksProxy = new SocksProxyImpl();
|
||||||
this._socksProxy.setPattern('*');
|
this._socksProxy.setPattern('*');
|
||||||
this._socksProxy.addListener(SocksProxy.Events.SocksRequested, async (payload: SocksSocketRequestedPayload) => {
|
this._socksProxy.addListener('socksRequested', async payload => {
|
||||||
this._socksProxy.socketConnected({
|
this._socksProxy.socketConnected({
|
||||||
uid: payload.uid,
|
uid: payload.uid,
|
||||||
host: '127.0.0.1',
|
host: '127.0.0.1',
|
||||||
port: 0,
|
port: 0,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
this._socksProxy.addListener(SocksProxy.Events.SocksData, async (payload: SocksSocketRequestedPayload) => {
|
this._socksProxy.addListener('socksData', async payload => {
|
||||||
const body = '<html><title>Served by the SOCKS proxy</title></html>';
|
const body = '<html><title>Served by the SOCKS proxy</title></html>';
|
||||||
const data = Buffer.from([
|
const data = Buffer.from([
|
||||||
'HTTP/1.1 200 OK',
|
'HTTP/1.1 200 OK',
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,9 @@
|
||||||
import { playwrightTest as it, expect } from '../config/browserTest';
|
import { playwrightTest as it, expect } from '../config/browserTest';
|
||||||
import net from 'net';
|
import net from 'net';
|
||||||
import type { SocksSocketClosedPayload, SocksSocketDataPayload, SocksSocketRequestedPayload } from '../../packages/playwright-core/src/common/socksProxy';
|
import type { SocksSocketClosedPayload, SocksSocketDataPayload, SocksSocketRequestedPayload } from '../../packages/playwright-core/src/common/socksProxy';
|
||||||
import { SocksProxy } from '../../packages/playwright-core/lib/common/socksProxy';
|
|
||||||
|
import type { SocksProxy } from '../../packages/playwright-core/src/common/socksProxy';
|
||||||
|
const SocksProxyImpl = require('../../packages/playwright-core/lib/common/socksProxy').SocksProxy as any as typeof SocksProxy;
|
||||||
|
|
||||||
it.skip(({ mode }) => mode.startsWith('service'));
|
it.skip(({ mode }) => mode.startsWith('service'));
|
||||||
|
|
||||||
|
|
@ -291,9 +293,9 @@ it('should use proxy with emulated user agent', async ({ browserType }) => {
|
||||||
|
|
||||||
async function setupSocksForwardingServer(port: number, forwardPort: number) {
|
async function setupSocksForwardingServer(port: number, forwardPort: number) {
|
||||||
const connections = new Map<string, net.Socket>();
|
const connections = new Map<string, net.Socket>();
|
||||||
const socksProxy = new SocksProxy();
|
const socksProxy = new SocksProxyImpl();
|
||||||
socksProxy.setPattern('*');
|
socksProxy.setPattern('*');
|
||||||
socksProxy.addListener(SocksProxy.Events.SocksRequested, async (payload: SocksSocketRequestedPayload) => {
|
socksProxy.addListener('socksRequested', async (payload: SocksSocketRequestedPayload) => {
|
||||||
if (!['127.0.0.1', 'fake-localhost-127-0-0-1.nip.io'].includes(payload.host) || payload.port !== 1337) {
|
if (!['127.0.0.1', 'fake-localhost-127-0-0-1.nip.io'].includes(payload.host) || payload.port !== 1337) {
|
||||||
socksProxy.sendSocketError({ uid: payload.uid, error: 'ECONNREFUSED' });
|
socksProxy.sendSocketError({ uid: payload.uid, error: 'ECONNREFUSED' });
|
||||||
return;
|
return;
|
||||||
|
|
@ -309,10 +311,10 @@ async function setupSocksForwardingServer(port: number, forwardPort: number) {
|
||||||
socksProxy.socketConnected({ uid: payload.uid, host: target.localAddress, port: target.localPort });
|
socksProxy.socketConnected({ uid: payload.uid, host: target.localAddress, port: target.localPort });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
socksProxy.addListener(SocksProxy.Events.SocksData, async (payload: SocksSocketDataPayload) => {
|
socksProxy.addListener('socksData', async (payload: SocksSocketDataPayload) => {
|
||||||
connections.get(payload.uid)?.write(payload.data);
|
connections.get(payload.uid)?.write(payload.data);
|
||||||
});
|
});
|
||||||
socksProxy.addListener(SocksProxy.Events.SocksClosed, (payload: SocksSocketClosedPayload) => {
|
socksProxy.addListener('socksClosed', (payload: SocksSocketClosedPayload) => {
|
||||||
connections.get(payload.uid)?.destroy();
|
connections.get(payload.uid)?.destroy();
|
||||||
connections.delete(payload.uid);
|
connections.delete(payload.uid);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue