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