chore: introduce sdk object base class (#5370)
This commit is contained in:
Родитель
909544907c
Коммит
0652f3251f
|
@ -31,8 +31,9 @@ import { createGuid } from './utils/utils';
|
|||
import { SelectorsDispatcher } from './dispatchers/selectorsDispatcher';
|
||||
import { Selectors } from './server/selectors';
|
||||
import { BrowserContext, Video } from './server/browserContext';
|
||||
import { StreamDispatcher } from './dispatchers/streamDispatcher';
|
||||
import { StreamDispatcher, StreamWrapper } from './dispatchers/streamDispatcher';
|
||||
import { ProtocolLogger } from './server/types';
|
||||
import { SdkObject } from './server/sdkObject';
|
||||
|
||||
export class BrowserServerLauncherImpl implements BrowserServerLauncher {
|
||||
private _browserType: BrowserType;
|
||||
|
@ -118,7 +119,7 @@ export class BrowserServerImpl extends EventEmitter implements BrowserServer {
|
|||
connection.dispatch(JSON.parse(Buffer.from(message).toString()));
|
||||
});
|
||||
socket.on('error', () => {});
|
||||
const selectors = new Selectors();
|
||||
const selectors = new Selectors(this._browser.options.rootSdkObject);
|
||||
const scope = connection.rootDispatcher();
|
||||
const remoteBrowser = new RemoteBrowserDispatcher(scope, this._browser, selectors);
|
||||
socket.on('close', () => {
|
||||
|
@ -130,12 +131,12 @@ export class BrowserServerImpl extends EventEmitter implements BrowserServer {
|
|||
}
|
||||
}
|
||||
|
||||
class RemoteBrowserDispatcher extends Dispatcher<{}, channels.RemoteBrowserInitializer> implements channels.PlaywrightChannel {
|
||||
class RemoteBrowserDispatcher extends Dispatcher<SdkObject, channels.RemoteBrowserInitializer> implements channels.PlaywrightChannel {
|
||||
readonly connectedBrowser: ConnectedBrowser;
|
||||
|
||||
constructor(scope: DispatcherScope, browser: Browser, selectors: Selectors) {
|
||||
const connectedBrowser = new ConnectedBrowser(scope, browser, selectors);
|
||||
super(scope, {}, 'RemoteBrowser', {
|
||||
super(scope, browser, 'RemoteBrowser', {
|
||||
selectors: new SelectorsDispatcher(scope, selectors),
|
||||
browser: connectedBrowser,
|
||||
}, false, 'remoteBrowser');
|
||||
|
@ -188,7 +189,7 @@ class ConnectedBrowser extends BrowserDispatcher {
|
|||
video._waitForCallbackOnFinish(async () => {
|
||||
const readable = fs.createReadStream(video._path);
|
||||
await new Promise(f => readable.on('readable', f));
|
||||
const stream = new StreamDispatcher(this._remoteBrowser!._scope, readable);
|
||||
const stream = new StreamDispatcher(this._remoteBrowser!._scope, new StreamWrapper(this._object, readable));
|
||||
this._remoteBrowser!._dispatchEvent('video', {
|
||||
stream,
|
||||
context: contextDispatcher,
|
||||
|
|
|
@ -35,6 +35,7 @@ export abstract class ChannelOwner<T extends channels.Channel = channels.Channel
|
|||
|
||||
constructor(parent: ChannelOwner | Connection, type: string, guid: string, initializer: Initializer) {
|
||||
super();
|
||||
this.setMaxListeners(0);
|
||||
this._connection = parent instanceof ChannelOwner ? parent._connection : parent;
|
||||
this._type = type;
|
||||
this._guid = guid;
|
||||
|
|
|
@ -97,7 +97,6 @@ export class Page extends ChannelOwner<channels.PageChannel, channels.PageInitia
|
|||
|
||||
constructor(parent: ChannelOwner, type: string, guid: string, initializer: channels.PageInitializer) {
|
||||
super(parent, type, guid, initializer);
|
||||
this.setMaxListeners(0);
|
||||
this._browserContext = parent as BrowserContext;
|
||||
this._timeoutSettings = new TimeoutSettings(this._browserContext._timeoutSettings);
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ import { createScheme, Validator, ValidationError } from '../protocol/validator'
|
|||
import { assert, createGuid, debugAssert, isUnderTest } from '../utils/utils';
|
||||
import { tOptional } from '../protocol/validatorPrimitives';
|
||||
import { kBrowserOrContextClosedError } from '../utils/errors';
|
||||
import { SdkObject } from '../server/sdkObject';
|
||||
|
||||
export const dispatcherSymbol = Symbol('dispatcher');
|
||||
|
||||
|
@ -38,7 +39,14 @@ export function lookupNullableDispatcher<DispatcherType>(object: any | null): Di
|
|||
return object ? lookupDispatcher(object) : undefined;
|
||||
}
|
||||
|
||||
export class Dispatcher<Type, Initializer> extends EventEmitter implements channels.Channel {
|
||||
export type CallMetadata = channels.Metadata & {
|
||||
object: SdkObject;
|
||||
type: string;
|
||||
method: string;
|
||||
params: any;
|
||||
};
|
||||
|
||||
export class Dispatcher<Type extends SdkObject, Initializer> extends EventEmitter implements channels.Channel {
|
||||
private _connection: DispatcherConnection;
|
||||
private _isScope: boolean;
|
||||
// Parent is always "isScope".
|
||||
|
@ -112,10 +120,9 @@ export class Dispatcher<Type, Initializer> extends EventEmitter implements chann
|
|||
}
|
||||
|
||||
export type DispatcherScope = Dispatcher<any, any>;
|
||||
|
||||
class Root extends Dispatcher<{}, {}> {
|
||||
class Root extends Dispatcher<SdkObject, {}> {
|
||||
constructor(connection: DispatcherConnection) {
|
||||
super(connection, {}, '', {}, true, '');
|
||||
super(connection, new SdkObject(null), '', {}, true, '');
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -178,7 +185,14 @@ export class DispatcherConnection {
|
|||
const validated = this._validateParams(dispatcher._type, method, params);
|
||||
if (typeof (dispatcher as any)[method] !== 'function')
|
||||
throw new Error(`Mismatching dispatcher: "${dispatcher._type}" does not implement "${method}"`);
|
||||
const result = await (dispatcher as any)[method](validated, this._validateMetadata(metadata));
|
||||
const callMetadata: CallMetadata = {
|
||||
...this._validateMetadata(metadata).stack,
|
||||
object: dispatcher._object,
|
||||
type: dispatcher._type,
|
||||
method,
|
||||
params,
|
||||
};
|
||||
const result = await (dispatcher as any)[method](validated, callMetadata);
|
||||
this.onmessage({ id, result: this._replaceDispatchersWithGuids(result) });
|
||||
} catch (e) {
|
||||
this.onmessage({ id, error: serializeError(e) });
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
import { Download } from '../server/download';
|
||||
import * as channels from '../protocol/channels';
|
||||
import { Dispatcher, DispatcherScope } from './dispatcher';
|
||||
import { StreamDispatcher } from './streamDispatcher';
|
||||
import { StreamDispatcher, StreamWrapper } from './streamDispatcher';
|
||||
import * as fs from 'fs';
|
||||
import * as util from 'util';
|
||||
import { mkdirIfNeeded } from '../utils/utils';
|
||||
|
@ -65,7 +65,7 @@ export class DownloadDispatcher extends Dispatcher<Download, channels.DownloadIn
|
|||
try {
|
||||
const readable = fs.createReadStream(localPath);
|
||||
await new Promise(f => readable.on('readable', f));
|
||||
const stream = new StreamDispatcher(this._scope, readable);
|
||||
const stream = new StreamDispatcher(this._scope, new StreamWrapper(this._object, readable));
|
||||
// Resolve with a stream, so that client starts saving the data.
|
||||
resolve({ stream });
|
||||
// Block the download until the stream is consumed.
|
||||
|
@ -87,7 +87,7 @@ export class DownloadDispatcher extends Dispatcher<Download, channels.DownloadIn
|
|||
return {};
|
||||
const readable = fs.createReadStream(fileName);
|
||||
await new Promise(f => readable.on('readable', f));
|
||||
return { stream: new StreamDispatcher(this._scope, readable) };
|
||||
return { stream: new StreamDispatcher(this._scope, new StreamWrapper(this._object, readable)) };
|
||||
}
|
||||
|
||||
async failure(): Promise<channels.DownloadFailureResult> {
|
||||
|
|
|
@ -31,6 +31,7 @@ import { ElementHandleDispatcher, createHandle } from './elementHandlerDispatche
|
|||
import { FileChooser } from '../server/fileChooser';
|
||||
import { CRCoverage } from '../server/chromium/crCoverage';
|
||||
import { JSHandle } from '../server/javascript';
|
||||
import { SdkObject } from '../server/sdkObject';
|
||||
|
||||
export class PageDispatcher extends Dispatcher<Page, channels.PageInitializer> implements channels.PageChannel {
|
||||
private _page: Page;
|
||||
|
@ -264,13 +265,13 @@ export class WorkerDispatcher extends Dispatcher<Worker, channels.WorkerInitiali
|
|||
}
|
||||
}
|
||||
|
||||
export class BindingCallDispatcher extends Dispatcher<{}, channels.BindingCallInitializer> implements channels.BindingCallChannel {
|
||||
export class BindingCallDispatcher extends Dispatcher<SdkObject, channels.BindingCallInitializer> implements channels.BindingCallChannel {
|
||||
private _resolve: ((arg: any) => void) | undefined;
|
||||
private _reject: ((error: any) => void) | undefined;
|
||||
private _promise: Promise<any>;
|
||||
|
||||
constructor(scope: DispatcherScope, name: string, needsHandle: boolean, source: { context: BrowserContext, page: Page, frame: Frame }, args: any[]) {
|
||||
super(scope, {}, 'BindingCall', {
|
||||
super(scope, new SdkObject(null), 'BindingCall', {
|
||||
frame: lookupDispatcher<FrameDispatcher>(source.frame),
|
||||
name,
|
||||
args: needsHandle ? undefined : args.map(serializeResult),
|
||||
|
|
|
@ -17,18 +17,27 @@
|
|||
import * as channels from '../protocol/channels';
|
||||
import { Dispatcher, DispatcherScope } from './dispatcher';
|
||||
import * as stream from 'stream';
|
||||
import { SdkObject } from '../server/sdkObject';
|
||||
|
||||
export class StreamDispatcher extends Dispatcher<stream.Readable, channels.StreamInitializer> implements channels.StreamChannel {
|
||||
constructor(scope: DispatcherScope, stream: stream.Readable) {
|
||||
export class StreamWrapper extends SdkObject {
|
||||
readonly stream: stream.Readable;
|
||||
constructor(parentObject: SdkObject, stream: stream.Readable) {
|
||||
super(parentObject);
|
||||
this.stream = stream;
|
||||
}
|
||||
}
|
||||
|
||||
export class StreamDispatcher extends Dispatcher<StreamWrapper, channels.StreamInitializer> implements channels.StreamChannel {
|
||||
constructor(scope: DispatcherScope, stream: StreamWrapper) {
|
||||
super(scope, stream, 'Stream', {});
|
||||
}
|
||||
|
||||
async read(params: channels.StreamReadParams): Promise<channels.StreamReadResult> {
|
||||
const buffer = this._object.read(Math.min(this._object.readableLength, params.size || this._object.readableLength));
|
||||
const buffer = this._object.stream.read(Math.min(this._object.stream.readableLength, params.size || this._object.stream.readableLength));
|
||||
return { binary: buffer ? buffer.toString('base64') : '' };
|
||||
}
|
||||
|
||||
async close() {
|
||||
this._object.destroy();
|
||||
this._object.stream.destroy();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,11 +32,12 @@ import { RecentLogsCollector } from '../../utils/debugLogger';
|
|||
import { TimeoutSettings } from '../../utils/timeoutSettings';
|
||||
import { AndroidWebView } from '../../protocol/channels';
|
||||
import { CRPage } from '../chromium/crPage';
|
||||
import { SdkObject } from '../sdkObject';
|
||||
|
||||
const readFileAsync = util.promisify(fs.readFile);
|
||||
|
||||
export interface Backend {
|
||||
devices(): Promise<DeviceBackend[]>;
|
||||
devices(owner: SdkObject): Promise<DeviceBackend[]>;
|
||||
}
|
||||
|
||||
export interface DeviceBackend {
|
||||
|
@ -44,22 +45,23 @@ export interface DeviceBackend {
|
|||
status: string;
|
||||
close(): Promise<void>;
|
||||
init(): Promise<void>;
|
||||
runCommand(command: string): Promise<Buffer>;
|
||||
open(command: string): Promise<SocketBackend>;
|
||||
runCommand(owner: SdkObject, command: string): Promise<Buffer>;
|
||||
open(owner: SdkObject, command: string): Promise<SocketBackend>;
|
||||
}
|
||||
|
||||
export interface SocketBackend extends EventEmitter {
|
||||
export interface SocketBackend extends SdkObject {
|
||||
write(data: Buffer): Promise<void>;
|
||||
close(): Promise<void>;
|
||||
}
|
||||
|
||||
export class Android {
|
||||
export class Android extends SdkObject {
|
||||
private _backend: Backend;
|
||||
private _devices = new Map<string, AndroidDevice>();
|
||||
readonly _timeoutSettings: TimeoutSettings;
|
||||
readonly _playwrightOptions: PlaywrightOptions;
|
||||
|
||||
constructor(backend: Backend, playwrightOptions: PlaywrightOptions) {
|
||||
super(playwrightOptions.rootSdkObject);
|
||||
this._backend = backend;
|
||||
this._playwrightOptions = playwrightOptions;
|
||||
this._timeoutSettings = new TimeoutSettings();
|
||||
|
@ -70,7 +72,7 @@ export class Android {
|
|||
}
|
||||
|
||||
async devices(): Promise<AndroidDevice[]> {
|
||||
const devices = (await this._backend.devices()).filter(d => d.status === 'device');
|
||||
const devices = (await this._backend.devices(this)).filter(d => d.status === 'device');
|
||||
const newSerials = new Set<string>();
|
||||
for (const d of devices) {
|
||||
newSerials.add(d.serial);
|
||||
|
@ -91,7 +93,7 @@ export class Android {
|
|||
}
|
||||
}
|
||||
|
||||
export class AndroidDevice extends EventEmitter {
|
||||
export class AndroidDevice extends SdkObject {
|
||||
readonly _backend: DeviceBackend;
|
||||
readonly model: string;
|
||||
readonly serial: string;
|
||||
|
@ -113,8 +115,7 @@ export class AndroidDevice extends EventEmitter {
|
|||
private _isClosed = false;
|
||||
|
||||
constructor(android: Android, backend: DeviceBackend, model: string) {
|
||||
super();
|
||||
this.setMaxListeners(0);
|
||||
super(android);
|
||||
this._android = android;
|
||||
this._backend = backend;
|
||||
this.model = model;
|
||||
|
@ -124,7 +125,7 @@ export class AndroidDevice extends EventEmitter {
|
|||
|
||||
static async create(android: Android, backend: DeviceBackend): Promise<AndroidDevice> {
|
||||
await backend.init();
|
||||
const model = await backend.runCommand('shell:getprop ro.product.model');
|
||||
const model = await backend.runCommand(android, 'shell:getprop ro.product.model');
|
||||
const device = new AndroidDevice(android, backend, model.toString().trim());
|
||||
await device._init();
|
||||
return device;
|
||||
|
@ -143,17 +144,17 @@ export class AndroidDevice extends EventEmitter {
|
|||
}
|
||||
|
||||
async shell(command: string): Promise<Buffer> {
|
||||
const result = await this._backend.runCommand(`shell:${command}`);
|
||||
const result = await this._backend.runCommand(this, `shell:${command}`);
|
||||
await this._refreshWebViews();
|
||||
return result;
|
||||
}
|
||||
|
||||
async open(command: string): Promise<SocketBackend> {
|
||||
return await this._backend.open(`${command}`);
|
||||
return await this._backend.open(this, `${command}`);
|
||||
}
|
||||
|
||||
async screenshot(): Promise<Buffer> {
|
||||
return await this._backend.runCommand(`shell:screencap -p`);
|
||||
return await this._backend.runCommand(this, `shell:screencap -p`);
|
||||
}
|
||||
|
||||
private async _driver(): Promise<Transport> {
|
||||
|
@ -198,7 +199,7 @@ export class AndroidDevice extends EventEmitter {
|
|||
debug('pw:android')(`Polling the socket localabstract:${socketName}`);
|
||||
while (!socket) {
|
||||
try {
|
||||
socket = await this._backend.open(`localabstract:${socketName}`);
|
||||
socket = await this._backend.open(this, `localabstract:${socketName}`);
|
||||
} catch (e) {
|
||||
await new Promise(f => setTimeout(f, 250));
|
||||
}
|
||||
|
@ -234,13 +235,13 @@ export class AndroidDevice extends EventEmitter {
|
|||
|
||||
async launchBrowser(pkg: string = 'com.android.chrome', options: types.BrowserContextOptions = {}): Promise<BrowserContext> {
|
||||
debug('pw:android')('Force-stopping', pkg);
|
||||
await this._backend.runCommand(`shell:am force-stop ${pkg}`);
|
||||
await this._backend.runCommand(this, `shell:am force-stop ${pkg}`);
|
||||
|
||||
const socketName = 'playwright-' + createGuid();
|
||||
const commandLine = `_ --disable-fre --no-default-browser-check --no-first-run --remote-debugging-socket-name=${socketName}`;
|
||||
debug('pw:android')('Starting', pkg, commandLine);
|
||||
await this._backend.runCommand(`shell:echo "${commandLine}" > /data/local/tmp/chrome-command-line`);
|
||||
await this._backend.runCommand(`shell:am start -n ${pkg}/com.google.android.apps.chrome.Main about:blank`);
|
||||
await this._backend.runCommand(this, `shell:echo "${commandLine}" > /data/local/tmp/chrome-command-line`);
|
||||
await this._backend.runCommand(this, `shell:am start -n ${pkg}/com.google.android.apps.chrome.Main about:blank`);
|
||||
return await this._connectToBrowser(socketName, options);
|
||||
}
|
||||
|
||||
|
@ -295,7 +296,7 @@ export class AndroidDevice extends EventEmitter {
|
|||
async installApk(content: Buffer, options?: { args?: string[] }): Promise<void> {
|
||||
const args = options && options.args ? options.args : ['-r', '-t', '-S'];
|
||||
debug('pw:android')('Opening install socket');
|
||||
const installSocket = await this._backend.open(`shell:cmd package install ${args.join(' ')} ${content.length}`);
|
||||
const installSocket = await this._backend.open(this, `shell:cmd package install ${args.join(' ')} ${content.length}`);
|
||||
debug('pw:android')('Writing driver bytes: ' + content.length);
|
||||
await installSocket.write(content);
|
||||
const success = await new Promise(f => installSocket.on('data', f));
|
||||
|
@ -304,7 +305,7 @@ export class AndroidDevice extends EventEmitter {
|
|||
}
|
||||
|
||||
async push(content: Buffer, path: string, mode = 0o644): Promise<void> {
|
||||
const socket = await this._backend.open(`sync:`);
|
||||
const socket = await this._backend.open(this, `sync:`);
|
||||
const sendHeader = async (command: string, length: number) => {
|
||||
const buffer = Buffer.alloc(command.length + 4);
|
||||
buffer.write(command, 0);
|
||||
|
@ -328,7 +329,7 @@ export class AndroidDevice extends EventEmitter {
|
|||
}
|
||||
|
||||
private async _refreshWebViews() {
|
||||
const sockets = (await this._backend.runCommand(`shell:cat /proc/net/unix | grep webview_devtools_remote`)).toString().split('\n');
|
||||
const sockets = (await this._backend.runCommand(this, `shell:cat /proc/net/unix | grep webview_devtools_remote`)).toString().split('\n');
|
||||
if (this._isClosed)
|
||||
return;
|
||||
|
||||
|
@ -344,7 +345,7 @@ export class AndroidDevice extends EventEmitter {
|
|||
if (this._webViews.has(pid))
|
||||
continue;
|
||||
|
||||
const procs = (await this._backend.runCommand(`shell:ps -A | grep ${pid}`)).toString().split('\n');
|
||||
const procs = (await this._backend.runCommand(this, `shell:ps -A | grep ${pid}`)).toString().split('\n');
|
||||
if (this._isClosed)
|
||||
return;
|
||||
let pkg = '';
|
||||
|
|
|
@ -17,12 +17,12 @@
|
|||
import * as assert from 'assert';
|
||||
import * as debug from 'debug';
|
||||
import * as net from 'net';
|
||||
import { EventEmitter } from 'ws';
|
||||
import { SdkObject } from '../sdkObject';
|
||||
import { Backend, DeviceBackend, SocketBackend } from './android';
|
||||
|
||||
export class AdbBackend implements Backend {
|
||||
async devices(): Promise<DeviceBackend[]> {
|
||||
const result = await runCommand('host:devices');
|
||||
async devices(sdkObject: SdkObject): Promise<DeviceBackend[]> {
|
||||
const result = await runCommand(sdkObject, 'host:devices');
|
||||
const lines = result.toString().trim().split('\n');
|
||||
return lines.map(line => {
|
||||
const [serial, status] = line.trim().split('\t');
|
||||
|
@ -46,20 +46,20 @@ class AdbDevice implements DeviceBackend {
|
|||
async close() {
|
||||
}
|
||||
|
||||
runCommand(command: string): Promise<Buffer> {
|
||||
return runCommand(command, this.serial);
|
||||
runCommand(sdkObject: SdkObject, command: string): Promise<Buffer> {
|
||||
return runCommand(sdkObject, command, this.serial);
|
||||
}
|
||||
|
||||
async open(command: string): Promise<SocketBackend> {
|
||||
const result = await open(command, this.serial);
|
||||
async open(sdkObject: SdkObject, command: string): Promise<SocketBackend> {
|
||||
const result = await open(sdkObject, command, this.serial);
|
||||
result.becomeSocket();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
async function runCommand(command: string, serial?: string): Promise<Buffer> {
|
||||
async function runCommand(sdkObject: SdkObject, command: string, serial?: string): Promise<Buffer> {
|
||||
debug('pw:adb:runCommand')(command, serial);
|
||||
const socket = new BufferedSocketWrapper(command, net.createConnection({ port: 5037 }));
|
||||
const socket = new BufferedSocketWrapper(sdkObject, command, net.createConnection({ port: 5037 }));
|
||||
if (serial) {
|
||||
await socket.write(encodeMessage(`host:transport:${serial}`));
|
||||
const status = await socket.read(4);
|
||||
|
@ -79,8 +79,8 @@ async function runCommand(command: string, serial?: string): Promise<Buffer> {
|
|||
return commandOutput;
|
||||
}
|
||||
|
||||
async function open(command: string, serial?: string): Promise<BufferedSocketWrapper> {
|
||||
const socket = new BufferedSocketWrapper(command, net.createConnection({ port: 5037 }));
|
||||
async function open(sdkObject: SdkObject, command: string, serial?: string): Promise<BufferedSocketWrapper> {
|
||||
const socket = new BufferedSocketWrapper(sdkObject, command, net.createConnection({ port: 5037 }));
|
||||
if (serial) {
|
||||
await socket.write(encodeMessage(`host:transport:${serial}`));
|
||||
const status = await socket.read(4);
|
||||
|
@ -98,7 +98,7 @@ function encodeMessage(message: string): Buffer {
|
|||
return Buffer.from(lenHex + message);
|
||||
}
|
||||
|
||||
class BufferedSocketWrapper extends EventEmitter implements SocketBackend {
|
||||
class BufferedSocketWrapper extends SdkObject implements SocketBackend {
|
||||
private _socket: net.Socket;
|
||||
private _buffer = Buffer.from([]);
|
||||
private _isSocket = false;
|
||||
|
@ -107,9 +107,8 @@ class BufferedSocketWrapper extends EventEmitter implements SocketBackend {
|
|||
private _isClosed = false;
|
||||
private _command: string;
|
||||
|
||||
constructor(command: string, socket: net.Socket) {
|
||||
super();
|
||||
this.setMaxListeners(0);
|
||||
constructor(parent: SdkObject, command: string, socket: net.Socket) {
|
||||
super(parent);
|
||||
this._command = command;
|
||||
this._socket = socket;
|
||||
this._connectPromise = new Promise(f => this._socket.on('connect', f));
|
||||
|
|
|
@ -17,12 +17,13 @@
|
|||
import * as types from './types';
|
||||
import { BrowserContext, ContextListener, Video } from './browserContext';
|
||||
import { Page } from './page';
|
||||
import { EventEmitter } from 'events';
|
||||
import { Download } from './download';
|
||||
import { ProxySettings } from './types';
|
||||
import { ChildProcess } from 'child_process';
|
||||
import { RecentLogsCollector } from '../utils/debugLogger';
|
||||
import * as registry from '../utils/registry';
|
||||
import { SdkObject } from './sdkObject';
|
||||
import { Selectors } from './selectors';
|
||||
|
||||
export interface BrowserProcess {
|
||||
onclose: ((exitCode: number | null, signal: string | null) => void) | undefined;
|
||||
|
@ -34,7 +35,10 @@ export interface BrowserProcess {
|
|||
export type PlaywrightOptions = {
|
||||
contextListeners: ContextListener[],
|
||||
registry: registry.Registry,
|
||||
isInternal: boolean
|
||||
isInternal: boolean,
|
||||
rootSdkObject: SdkObject,
|
||||
// FIXME, this is suspicious
|
||||
selectors: Selectors
|
||||
};
|
||||
|
||||
export type BrowserOptions = PlaywrightOptions & {
|
||||
|
@ -50,7 +54,7 @@ export type BrowserOptions = PlaywrightOptions & {
|
|||
slowMo?: number,
|
||||
};
|
||||
|
||||
export abstract class Browser extends EventEmitter {
|
||||
export abstract class Browser extends SdkObject {
|
||||
static Events = {
|
||||
Disconnected: 'disconnected',
|
||||
};
|
||||
|
@ -62,8 +66,8 @@ export abstract class Browser extends EventEmitter {
|
|||
readonly _idToVideo = new Map<string, Video>();
|
||||
|
||||
constructor(options: BrowserOptions) {
|
||||
super();
|
||||
this.setMaxListeners(0);
|
||||
super(options.rootSdkObject);
|
||||
this.attribution.browser = this;
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
|
|
|
@ -15,7 +15,6 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { EventEmitter } from 'events';
|
||||
import { TimeoutSettings } from '../utils/timeoutSettings';
|
||||
import { mkdirIfNeeded } from '../utils/utils';
|
||||
import { Browser, BrowserOptions } from './browser';
|
||||
|
@ -26,9 +25,10 @@ import { helper } from './helper';
|
|||
import * as network from './network';
|
||||
import { Page, PageBinding, PageDelegate } from './page';
|
||||
import { Progress, ProgressController, ProgressResult } from './progress';
|
||||
import { Selectors, serverSelectors } from './selectors';
|
||||
import { Selectors } from './selectors';
|
||||
import * as types from './types';
|
||||
import * as path from 'path';
|
||||
import { SdkObject } from './sdkObject';
|
||||
|
||||
export class Video {
|
||||
readonly _videoId: string;
|
||||
|
@ -94,7 +94,7 @@ export interface ContextListener {
|
|||
onContextDidDestroy(context: BrowserContext): Promise<void>;
|
||||
}
|
||||
|
||||
export abstract class BrowserContext extends EventEmitter {
|
||||
export abstract class BrowserContext extends SdkObject {
|
||||
static Events = {
|
||||
Close: 'close',
|
||||
Page: 'page',
|
||||
|
@ -122,8 +122,8 @@ export abstract class BrowserContext extends EventEmitter {
|
|||
terminalSize: { rows?: number, columns?: number } = {};
|
||||
|
||||
constructor(browser: Browser, options: types.BrowserContextOptions, browserContextId: string | undefined) {
|
||||
super();
|
||||
this.setMaxListeners(0);
|
||||
super(browser);
|
||||
this.attribution.context = this;
|
||||
this._browser = browser;
|
||||
this._options = options;
|
||||
this._browserContextId = browserContextId;
|
||||
|
@ -135,8 +135,8 @@ export abstract class BrowserContext extends EventEmitter {
|
|||
this._selectors = selectors;
|
||||
}
|
||||
|
||||
selectors() {
|
||||
return this._selectors || serverSelectors;
|
||||
selectors(): Selectors {
|
||||
return this._selectors || this._browser.options.selectors;
|
||||
}
|
||||
|
||||
async _initialize() {
|
||||
|
|
|
@ -31,18 +31,21 @@ import { validateHostRequirements } from './validateDependencies';
|
|||
import { isDebugMode } from '../utils/utils';
|
||||
import { helper } from './helper';
|
||||
import { RecentLogsCollector } from '../utils/debugLogger';
|
||||
import { SdkObject } from './sdkObject';
|
||||
|
||||
const mkdirAsync = util.promisify(fs.mkdir);
|
||||
const mkdtempAsync = util.promisify(fs.mkdtemp);
|
||||
const existsAsync = (path: string): Promise<boolean> => new Promise(resolve => fs.stat(path, err => resolve(!err)));
|
||||
const DOWNLOADS_FOLDER = path.join(os.tmpdir(), 'playwright_downloads-');
|
||||
|
||||
export abstract class BrowserType {
|
||||
export abstract class BrowserType extends SdkObject {
|
||||
private _name: registry.BrowserName;
|
||||
readonly _registry: registry.Registry;
|
||||
readonly _playwrightOptions: PlaywrightOptions;
|
||||
|
||||
constructor(browserName: registry.BrowserName, playwrightOptions: PlaywrightOptions) {
|
||||
super(playwrightOptions.rootSdkObject);
|
||||
this.attribution.browserType = this;
|
||||
this._playwrightOptions = playwrightOptions;
|
||||
this._name = browserName;
|
||||
this._registry = playwrightOptions.registry;
|
||||
|
|
|
@ -263,7 +263,7 @@ class CRServiceWorker extends Worker {
|
|||
readonly _browserContext: CRBrowserContext;
|
||||
|
||||
constructor(browserContext: CRBrowserContext, session: CRSession, url: string) {
|
||||
super(url);
|
||||
super(browserContext, url);
|
||||
this._browserContext = browserContext;
|
||||
session.once('Runtime.executionContextCreated', event => {
|
||||
this._createExecutionContext(new CRExecutionContext(session, event.context));
|
||||
|
|
|
@ -23,6 +23,7 @@ import { rewriteErrorMessage } from '../../utils/stackTrace';
|
|||
import { debugLogger, RecentLogsCollector } from '../../utils/debugLogger';
|
||||
import { ProtocolLogger } from '../types';
|
||||
import { helper } from '../helper';
|
||||
import { SdkObject } from '../sdkObject';
|
||||
|
||||
export const ConnectionEvents = {
|
||||
Disconnected: Symbol('ConnectionEvents.Disconnected')
|
||||
|
@ -123,7 +124,7 @@ export const CRSessionEvents = {
|
|||
Disconnected: Symbol('Events.CDPSession.Disconnected')
|
||||
};
|
||||
|
||||
export class CRSession extends EventEmitter {
|
||||
export class CRSession extends SdkObject {
|
||||
_connection: CRConnection | null;
|
||||
_eventListener?: (method: string, params?: Object) => void;
|
||||
private readonly _callbacks = new Map<number, {resolve: (o: any) => void, reject: (e: Error) => void, error: Error, method: string}>();
|
||||
|
@ -139,8 +140,7 @@ export class CRSession extends EventEmitter {
|
|||
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, rootSessionId: string, targetType: string, sessionId: string) {
|
||||
super();
|
||||
this.setMaxListeners(0);
|
||||
super(null);
|
||||
this._connection = connection;
|
||||
this._rootSessionId = rootSessionId;
|
||||
this._targetType = targetType;
|
||||
|
|
|
@ -639,7 +639,7 @@ class FrameSession {
|
|||
}
|
||||
|
||||
const url = event.targetInfo.url;
|
||||
const worker = new Worker(url);
|
||||
const worker = new Worker(this._page, url);
|
||||
this._page._addWorker(event.sessionId, worker);
|
||||
session.once('Runtime.executionContextCreated', async event => {
|
||||
worker._createExecutionContext(new CRExecutionContext(session, event.context));
|
||||
|
@ -759,7 +759,7 @@ class FrameSession {
|
|||
lineNumber: lineNumber || 0,
|
||||
columnNumber: 0,
|
||||
};
|
||||
this._page.emit(Page.Events.Console, new ConsoleMessage(level, text, [], location));
|
||||
this._page.emit(Page.Events.Console, new ConsoleMessage(this._page, level, text, [], location));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -15,15 +15,17 @@
|
|||
*/
|
||||
|
||||
import * as js from './javascript';
|
||||
import { SdkObject } from './sdkObject';
|
||||
import { ConsoleMessageLocation } from './types';
|
||||
|
||||
export class ConsoleMessage {
|
||||
export class ConsoleMessage extends SdkObject {
|
||||
private _type: string;
|
||||
private _text?: string;
|
||||
private _args: js.JSHandle[];
|
||||
private _location: ConsoleMessageLocation;
|
||||
|
||||
constructor(type: string, text: string | undefined, args: js.JSHandle[], location?: ConsoleMessageLocation) {
|
||||
constructor(parent: SdkObject, type: string, text: string | undefined, args: js.JSHandle[], location?: ConsoleMessageLocation) {
|
||||
super(parent);
|
||||
this._type = type;
|
||||
this._text = text;
|
||||
this._args = args;
|
||||
|
|
|
@ -17,12 +17,13 @@
|
|||
|
||||
import { assert } from '../utils/utils';
|
||||
import { Page } from './page';
|
||||
import { SdkObject } from './sdkObject';
|
||||
|
||||
type OnHandle = (accept: boolean, promptText?: string) => Promise<void>;
|
||||
|
||||
export type DialogType = 'alert' | 'beforeunload' | 'confirm' | 'prompt';
|
||||
|
||||
export class Dialog {
|
||||
export class Dialog extends SdkObject {
|
||||
private _page: Page;
|
||||
private _type: string;
|
||||
private _message: string;
|
||||
|
@ -31,6 +32,7 @@ export class Dialog {
|
|||
private _defaultValue: string;
|
||||
|
||||
constructor(page: Page, type: string, message: string, onHandle: OnHandle, defaultValue?: string) {
|
||||
super(page);
|
||||
this._page = page;
|
||||
this._type = type;
|
||||
this._message = message;
|
||||
|
|
|
@ -31,7 +31,7 @@ export class FrameExecutionContext extends js.ExecutionContext {
|
|||
readonly world: types.World | null;
|
||||
|
||||
constructor(delegate: js.ExecutionContextDelegate, frame: frames.Frame, world: types.World|null) {
|
||||
super(delegate);
|
||||
super(frame, delegate);
|
||||
this.frame = frame;
|
||||
this.world = world;
|
||||
}
|
||||
|
|
|
@ -19,10 +19,11 @@ import * as fs from 'fs';
|
|||
import * as util from 'util';
|
||||
import { Page } from './page';
|
||||
import { assert } from '../utils/utils';
|
||||
import { SdkObject } from './sdkObject';
|
||||
|
||||
type SaveCallback = (localPath: string, error?: string) => Promise<void>;
|
||||
|
||||
export class Download {
|
||||
export class Download extends SdkObject {
|
||||
private _downloadsPath: string;
|
||||
private _uuid: string;
|
||||
private _finishedCallback: () => void;
|
||||
|
@ -37,6 +38,7 @@ export class Download {
|
|||
private _suggestedFilename: string | undefined;
|
||||
|
||||
constructor(page: Page, downloadsPath: string, uuid: string, url: string, suggestedFilename?: string) {
|
||||
super(page);
|
||||
this._page = page;
|
||||
this._downloadsPath = downloadsPath;
|
||||
this._uuid = uuid;
|
||||
|
|
|
@ -27,12 +27,12 @@ import { launchProcess, envArrayToObject } from '../processLauncher';
|
|||
import { BrowserContext } from '../browserContext';
|
||||
import type {BrowserWindow} from 'electron';
|
||||
import { Progress, ProgressController, runAbortableTask } from '../progress';
|
||||
import { EventEmitter } from 'events';
|
||||
import { helper } from '../helper';
|
||||
import { BrowserOptions, BrowserProcess, PlaywrightOptions } from '../browser';
|
||||
import * as childProcess from 'child_process';
|
||||
import * as readline from 'readline';
|
||||
import { RecentLogsCollector } from '../../utils/debugLogger';
|
||||
import { SdkObject } from '../sdkObject';
|
||||
|
||||
export type ElectronLaunchOptionsBase = {
|
||||
executablePath?: string,
|
||||
|
@ -47,7 +47,7 @@ export interface ElectronPage extends Page {
|
|||
_browserWindowId: number;
|
||||
}
|
||||
|
||||
export class ElectronApplication extends EventEmitter {
|
||||
export class ElectronApplication extends SdkObject {
|
||||
static Events = {
|
||||
Close: 'close',
|
||||
Window: 'window',
|
||||
|
@ -62,9 +62,8 @@ export class ElectronApplication extends EventEmitter {
|
|||
private _lastWindowId = 0;
|
||||
readonly _timeoutSettings = new TimeoutSettings();
|
||||
|
||||
constructor(browser: CRBrowser, nodeConnection: CRConnection) {
|
||||
super();
|
||||
this.setMaxListeners(0);
|
||||
constructor(parent: SdkObject, browser: CRBrowser, nodeConnection: CRConnection) {
|
||||
super(parent);
|
||||
this._browserContext = browser._defaultContext as CRBrowserContext;
|
||||
this._browserContext.on(BrowserContext.Events.Close, () => {
|
||||
// Emit application closed after context closed.
|
||||
|
@ -115,17 +114,18 @@ export class ElectronApplication extends EventEmitter {
|
|||
async _init() {
|
||||
this._nodeSession.on('Runtime.executionContextCreated', (event: any) => {
|
||||
if (event.context.auxData && event.context.auxData.isDefault)
|
||||
this._nodeExecutionContext = new js.ExecutionContext(new CRExecutionContext(this._nodeSession, event.context));
|
||||
this._nodeExecutionContext = new js.ExecutionContext(this, new CRExecutionContext(this._nodeSession, event.context));
|
||||
});
|
||||
await this._nodeSession.send('Runtime.enable', {}).catch(e => {});
|
||||
this._nodeElectronHandle = await js.evaluate(this._nodeExecutionContext!, false /* returnByValue */, `process.mainModule.require('electron')`);
|
||||
}
|
||||
}
|
||||
|
||||
export class Electron {
|
||||
export class Electron extends SdkObject {
|
||||
private _playwrightOptions: PlaywrightOptions;
|
||||
|
||||
constructor(playwrightOptions: PlaywrightOptions) {
|
||||
super(playwrightOptions.rootSdkObject);
|
||||
this._playwrightOptions = playwrightOptions;
|
||||
}
|
||||
|
||||
|
@ -187,7 +187,7 @@ export class Electron {
|
|||
browserLogsCollector,
|
||||
};
|
||||
const browser = await CRBrowser.connect(chromeTransport, browserOptions);
|
||||
app = new ElectronApplication(browser, nodeConnection);
|
||||
app = new ElectronApplication(this, browser, nodeConnection);
|
||||
await app._init();
|
||||
return app;
|
||||
}, TimeoutSettings.timeout(options));
|
||||
|
|
|
@ -244,7 +244,7 @@ export class FFPage implements PageDelegate {
|
|||
|
||||
async _onWorkerCreated(event: Protocol.Page.workerCreatedPayload) {
|
||||
const workerId = event.workerId;
|
||||
const worker = new Worker(event.url);
|
||||
const worker = new Worker(this._page, event.url);
|
||||
const workerSession = new FFSession(this._session._connection, 'worker', workerId, (message: any) => {
|
||||
this._session.send('Page.sendMessageToWorker', {
|
||||
frameId: event.frameId,
|
||||
|
|
|
@ -24,9 +24,9 @@ import { Page } from './page';
|
|||
import * as types from './types';
|
||||
import { BrowserContext } from './browserContext';
|
||||
import { Progress, ProgressController, runAbortableTask } from './progress';
|
||||
import { EventEmitter } from 'events';
|
||||
import { assert, makeWaitForNextTask } from '../utils/utils';
|
||||
import { debugLogger } from '../utils/debugLogger';
|
||||
import { SdkObject } from './sdkObject';
|
||||
|
||||
type ContextData = {
|
||||
contextPromise: Promise<dom.FrameExecutionContext>;
|
||||
|
@ -342,7 +342,7 @@ export class FrameManager {
|
|||
}
|
||||
|
||||
onWebSocketCreated(requestId: string, url: string) {
|
||||
const ws = new network.WebSocket(url);
|
||||
const ws = new network.WebSocket(this._page, url);
|
||||
this._webSockets.set(requestId, ws);
|
||||
}
|
||||
|
||||
|
@ -386,7 +386,7 @@ export class FrameManager {
|
|||
}
|
||||
}
|
||||
|
||||
export class Frame extends EventEmitter {
|
||||
export class Frame extends SdkObject {
|
||||
static Events = {
|
||||
Navigation: 'navigation',
|
||||
AddLifecycle: 'addlifecycle',
|
||||
|
@ -412,8 +412,8 @@ export class Frame extends EventEmitter {
|
|||
private _detachedCallback = () => {};
|
||||
|
||||
constructor(page: Page, id: string, parentFrame: Frame | null) {
|
||||
super();
|
||||
this.setMaxListeners(0);
|
||||
super(page);
|
||||
this.attribution.frame = this;
|
||||
this._id = id;
|
||||
this._page = page;
|
||||
this._parentFrame = parentFrame;
|
||||
|
|
|
@ -18,6 +18,7 @@ import * as dom from './dom';
|
|||
import * as utilityScriptSource from '../generated/utilityScriptSource';
|
||||
import { serializeAsCallArgument } from './common/utilityScriptSerializers';
|
||||
import type UtilityScript from './injected/utilityScript';
|
||||
import { SdkObject } from './sdkObject';
|
||||
|
||||
type ObjectId = string;
|
||||
export type RemoteObject = {
|
||||
|
@ -49,11 +50,12 @@ export interface ExecutionContextDelegate {
|
|||
releaseHandle(handle: JSHandle): Promise<void>;
|
||||
}
|
||||
|
||||
export class ExecutionContext {
|
||||
export class ExecutionContext extends SdkObject {
|
||||
readonly _delegate: ExecutionContextDelegate;
|
||||
private _utilityScriptPromise: Promise<JSHandle> | undefined;
|
||||
|
||||
constructor(delegate: ExecutionContextDelegate) {
|
||||
constructor(parent: SdkObject, delegate: ExecutionContextDelegate) {
|
||||
super(parent);
|
||||
this._delegate = delegate;
|
||||
}
|
||||
|
||||
|
@ -82,7 +84,7 @@ export class ExecutionContext {
|
|||
}
|
||||
}
|
||||
|
||||
export class JSHandle<T = any> {
|
||||
export class JSHandle<T = any> extends SdkObject {
|
||||
readonly _context: ExecutionContext;
|
||||
_disposed = false;
|
||||
readonly _objectId: ObjectId | undefined;
|
||||
|
@ -92,6 +94,7 @@ export class JSHandle<T = any> {
|
|||
private _previewCallback: ((preview: string) => void) | undefined;
|
||||
|
||||
constructor(context: ExecutionContext, type: string, objectId?: ObjectId, value?: any) {
|
||||
super(context);
|
||||
this._context = context;
|
||||
this._objectId = objectId;
|
||||
this._value = value;
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
import * as frames from './frames';
|
||||
import * as types from './types';
|
||||
import { assert } from '../utils/utils';
|
||||
import { EventEmitter } from 'events';
|
||||
import { SdkObject } from './sdkObject';
|
||||
|
||||
export function filterCookies(cookies: types.NetworkCookie[], urls: string[]): types.NetworkCookie[] {
|
||||
const parsedURLs = urls.map(s => new URL(s));
|
||||
|
@ -78,7 +78,7 @@ export function stripFragmentFromUrl(url: string): string {
|
|||
return url.substring(0, url.indexOf('#'));
|
||||
}
|
||||
|
||||
export class Request {
|
||||
export class Request extends SdkObject {
|
||||
readonly _routeDelegate: RouteDelegate | null;
|
||||
private _response: Response | null = null;
|
||||
private _redirectedFrom: Request | null;
|
||||
|
@ -99,6 +99,7 @@ export class Request {
|
|||
|
||||
constructor(routeDelegate: RouteDelegate | null, frame: frames.Frame, redirectedFrom: Request | null, documentId: string | undefined,
|
||||
url: string, resourceType: string, method: string, postData: Buffer | null, headers: types.HeadersArray) {
|
||||
super(frame);
|
||||
assert(!url.startsWith('data:'), 'Data urls should not fire requests');
|
||||
assert(!(routeDelegate && redirectedFrom), 'Should not be able to intercept redirects');
|
||||
this._routeDelegate = routeDelegate;
|
||||
|
@ -203,12 +204,13 @@ export class Request {
|
|||
}
|
||||
}
|
||||
|
||||
export class Route {
|
||||
export class Route extends SdkObject {
|
||||
private readonly _request: Request;
|
||||
private readonly _delegate: RouteDelegate;
|
||||
private _handled = false;
|
||||
|
||||
constructor(request: Request, delegate: RouteDelegate) {
|
||||
super(request.frame());
|
||||
this._request = request;
|
||||
this._delegate = delegate;
|
||||
}
|
||||
|
@ -261,7 +263,7 @@ export type ResourceTiming = {
|
|||
responseStart: number;
|
||||
};
|
||||
|
||||
export class Response {
|
||||
export class Response extends SdkObject {
|
||||
private _request: Request;
|
||||
private _contentPromise: Promise<Buffer> | null = null;
|
||||
_finishedPromise: Promise<{ error?: string }>;
|
||||
|
@ -275,6 +277,7 @@ export class Response {
|
|||
private _timing: ResourceTiming;
|
||||
|
||||
constructor(request: Request, status: number, statusText: string, headers: types.HeadersArray, timing: ResourceTiming, getResponseBodyCallback: GetResponseBodyCallback) {
|
||||
super(request.frame());
|
||||
this._request = request;
|
||||
this._timing = timing;
|
||||
this._status = status;
|
||||
|
@ -343,7 +346,7 @@ export class Response {
|
|||
}
|
||||
}
|
||||
|
||||
export class WebSocket extends EventEmitter {
|
||||
export class WebSocket extends SdkObject {
|
||||
private _url: string;
|
||||
|
||||
static Events = {
|
||||
|
@ -353,9 +356,8 @@ export class WebSocket extends EventEmitter {
|
|||
FrameSent: 'framesent',
|
||||
};
|
||||
|
||||
constructor(url: string) {
|
||||
super();
|
||||
this.setMaxListeners(0);
|
||||
constructor(parent: SdkObject, url: string) {
|
||||
super(parent);
|
||||
this._url = url;
|
||||
}
|
||||
|
||||
|
|
|
@ -26,12 +26,12 @@ import * as types from './types';
|
|||
import { BrowserContext, Video } from './browserContext';
|
||||
import { ConsoleMessage } from './console';
|
||||
import * as accessibility from './accessibility';
|
||||
import { EventEmitter } from 'events';
|
||||
import { FileChooser } from './fileChooser';
|
||||
import { ProgressController, runAbortableTask } from './progress';
|
||||
import { assert, isError } from '../utils/utils';
|
||||
import { debugLogger } from '../utils/debugLogger';
|
||||
import { Selectors } from './selectors';
|
||||
import { SdkObject } from './sdkObject';
|
||||
|
||||
export interface PageDelegate {
|
||||
readonly rawMouse: input.RawMouse;
|
||||
|
@ -92,7 +92,7 @@ type PageState = {
|
|||
extraHTTPHeaders: types.HeadersArray | null;
|
||||
};
|
||||
|
||||
export class Page extends EventEmitter {
|
||||
export class Page extends SdkObject {
|
||||
static Events = {
|
||||
Close: 'close',
|
||||
Crash: 'crash',
|
||||
|
@ -149,8 +149,8 @@ export class Page extends EventEmitter {
|
|||
_video: Video | null = null;
|
||||
|
||||
constructor(delegate: PageDelegate, browserContext: BrowserContext) {
|
||||
super();
|
||||
this.setMaxListeners(0);
|
||||
super(browserContext);
|
||||
this.attribution.page = this;
|
||||
this._delegate = delegate;
|
||||
this._closedCallback = () => {};
|
||||
this._closedPromise = new Promise(f => this._closedCallback = f);
|
||||
|
@ -288,7 +288,7 @@ export class Page extends EventEmitter {
|
|||
}
|
||||
|
||||
_addConsoleMessage(type: string, args: js.JSHandle[], location: types.ConsoleMessageLocation, text?: string) {
|
||||
const message = new ConsoleMessage(type, text, args, location);
|
||||
const message = new ConsoleMessage(this, type, text, args, location);
|
||||
const intercepted = this._frameManager.interceptConsoleMessage(message);
|
||||
if (intercepted || !this.listenerCount(Page.Events.Console))
|
||||
args.forEach(arg => arg.dispose());
|
||||
|
@ -502,7 +502,7 @@ export class Page extends EventEmitter {
|
|||
}
|
||||
}
|
||||
|
||||
export class Worker extends EventEmitter {
|
||||
export class Worker extends SdkObject {
|
||||
static Events = {
|
||||
Close: 'close',
|
||||
};
|
||||
|
@ -512,16 +512,15 @@ export class Worker extends EventEmitter {
|
|||
private _executionContextCallback: (value: js.ExecutionContext) => void;
|
||||
_existingExecutionContext: js.ExecutionContext | null = null;
|
||||
|
||||
constructor(url: string) {
|
||||
super();
|
||||
this.setMaxListeners(0);
|
||||
constructor(parent: SdkObject, url: string) {
|
||||
super(parent);
|
||||
this._url = url;
|
||||
this._executionContextCallback = () => {};
|
||||
this._executionContextPromise = new Promise(x => this._executionContextCallback = x);
|
||||
}
|
||||
|
||||
_createExecutionContext(delegate: js.ExecutionContextDelegate) {
|
||||
this._existingExecutionContext = new js.ExecutionContext(delegate);
|
||||
this._existingExecutionContext = new js.ExecutionContext(this, delegate);
|
||||
this._executionContextCallback(this._existingExecutionContext);
|
||||
}
|
||||
|
||||
|
|
|
@ -22,14 +22,15 @@ import { PlaywrightOptions } from './browser';
|
|||
import { Chromium } from './chromium/chromium';
|
||||
import { Electron } from './electron/electron';
|
||||
import { Firefox } from './firefox/firefox';
|
||||
import { serverSelectors } from './selectors';
|
||||
import { Selectors } from './selectors';
|
||||
import { HarTracer } from './supplements/har/harTracer';
|
||||
import { InspectorController } from './supplements/inspectorController';
|
||||
import { WebKit } from './webkit/webkit';
|
||||
import { Registry } from '../utils/registry';
|
||||
import { SdkObject } from './sdkObject';
|
||||
|
||||
export class Playwright {
|
||||
readonly selectors = serverSelectors;
|
||||
export class Playwright extends SdkObject {
|
||||
readonly selectors: Selectors;
|
||||
readonly chromium: Chromium;
|
||||
readonly android: Android;
|
||||
readonly electron: Electron;
|
||||
|
@ -38,6 +39,8 @@ export class Playwright {
|
|||
readonly options: PlaywrightOptions;
|
||||
|
||||
constructor(isInternal: boolean) {
|
||||
super(null);
|
||||
this.selectors = new Selectors(this);
|
||||
this.options = {
|
||||
isInternal,
|
||||
registry: new Registry(path.join(__dirname, '..', '..')),
|
||||
|
@ -46,7 +49,9 @@ export class Playwright {
|
|||
new InspectorController(),
|
||||
new Tracer(),
|
||||
new HarTracer()
|
||||
]
|
||||
],
|
||||
rootSdkObject: this,
|
||||
selectors: this.selectors
|
||||
};
|
||||
this.chromium = new Chromium(this.options);
|
||||
this.firefox = new Firefox(this.options);
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { EventEmitter } from 'events';
|
||||
import type { Browser } from './browser';
|
||||
import type { BrowserContext } from './browserContext';
|
||||
import type { BrowserType } from './browserType';
|
||||
import type { Frame } from './frames';
|
||||
import type { Page } from './page';
|
||||
|
||||
export type Attribution = {
|
||||
browserType?: BrowserType;
|
||||
browser?: Browser;
|
||||
context?: BrowserContext;
|
||||
page?: Page;
|
||||
frame?: Frame;
|
||||
};
|
||||
|
||||
export class SdkObject extends EventEmitter {
|
||||
attribution: Attribution;
|
||||
constructor(parent: SdkObject | null) {
|
||||
super();
|
||||
this.setMaxListeners(0);
|
||||
this.attribution = { ...parent?.attribution };
|
||||
}
|
||||
}
|
|
@ -19,6 +19,7 @@ import * as frames from './frames';
|
|||
import * as js from './javascript';
|
||||
import * as types from './types';
|
||||
import { ParsedSelector, parseSelector } from './common/selectorParser';
|
||||
import { SdkObject } from './sdkObject';
|
||||
|
||||
export type SelectorInfo = {
|
||||
parsed: ParsedSelector,
|
||||
|
@ -26,11 +27,12 @@ export type SelectorInfo = {
|
|||
selector: string,
|
||||
};
|
||||
|
||||
export class Selectors {
|
||||
export class Selectors extends SdkObject {
|
||||
readonly _builtinEngines: Set<string>;
|
||||
readonly _engines: Map<string, { source: string, contentScript: boolean }>;
|
||||
|
||||
constructor() {
|
||||
constructor(parent: SdkObject) {
|
||||
super(parent);
|
||||
// Note: keep in sync with InjectedScript class.
|
||||
this._builtinEngines = new Set([
|
||||
'css', 'css:light',
|
||||
|
@ -134,4 +136,6 @@ export class Selectors {
|
|||
}
|
||||
}
|
||||
|
||||
export const serverSelectors = new Selectors();
|
||||
export function serverSelectors(parent: SdkObject) {
|
||||
return new Selectors(parent);
|
||||
}
|
||||
|
|
|
@ -189,7 +189,7 @@ function isSharedLib(basename: string) {
|
|||
async function executablesOrSharedLibraries(directoryPath: string): Promise<string[]> {
|
||||
const allPaths = (await readdirAsync(directoryPath)).map(file => path.resolve(directoryPath, file));
|
||||
const allStats = await Promise.all(allPaths.map(aPath => statAsync(aPath)));
|
||||
const filePaths = allPaths.filter((aPath, index) => allStats[index].isFile());
|
||||
const filePaths = allPaths.filter((aPath, index) => (allStats[index] as any).isFile());
|
||||
|
||||
const executablersOrLibraries = (await Promise.all(filePaths.map(async filePath => {
|
||||
const basename = path.basename(filePath).toLowerCase();
|
||||
|
|
|
@ -35,7 +35,7 @@ export class WKWorkers {
|
|||
this.clear();
|
||||
this._sessionListeners = [
|
||||
helper.addEventListener(session, 'Worker.workerCreated', (event: Protocol.Worker.workerCreatedPayload) => {
|
||||
const worker = new Worker(event.url);
|
||||
const worker = new Worker(this._page, event.url);
|
||||
const workerSession = new WKSession(session.connection, event.workerId, 'Most likely the worker has been closed.', (message: any) => {
|
||||
session.send('Worker.sendMessageToWorker', {
|
||||
workerId: event.workerId,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "ESNext",
|
||||
"target": "es2018",
|
||||
"module": "commonjs",
|
||||
"lib": ["esnext", "dom", "DOM.Iterable"],
|
||||
"sourceMap": true,
|
||||
|
|
Загрузка…
Ссылка в новой задаче