chore: dispatcher is a scope (#16843)

This commit is contained in:
Pavel Feldman 2022-08-26 09:30:27 -07:00 committed by GitHub
parent aac9df0542
commit 6319b14069
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 108 additions and 111 deletions

View file

@ -22,16 +22,16 @@ import type * as channels from '../../protocol/channels';
import { BrowserContextDispatcher } from './browserContextDispatcher'; import { BrowserContextDispatcher } from './browserContextDispatcher';
import type { CallMetadata } from '../instrumentation'; import type { CallMetadata } from '../instrumentation';
export class AndroidDispatcher extends Dispatcher<Android, channels.AndroidChannel, RootDispatcher, AndroidDispatcher> implements channels.AndroidChannel { export class AndroidDispatcher extends Dispatcher<Android, channels.AndroidChannel, RootDispatcher> implements channels.AndroidChannel {
_type_Android = true; _type_Android = true;
constructor(scope: RootDispatcher, android: Android) { constructor(scope: RootDispatcher, android: Android) {
super(scope, android, 'Android', {}, true); super(scope, android, 'Android', {});
} }
async devices(params: channels.AndroidDevicesParams): Promise<channels.AndroidDevicesResult> { async devices(params: channels.AndroidDevicesParams): Promise<channels.AndroidDevicesResult> {
const devices = await this._object.devices(params); const devices = await this._object.devices(params);
return { return {
devices: devices.map(d => AndroidDeviceDispatcher.from(this._scope, d)) devices: devices.map(d => AndroidDeviceDispatcher.from(this, d))
}; };
} }
@ -40,7 +40,7 @@ export class AndroidDispatcher extends Dispatcher<Android, channels.AndroidChann
} }
} }
export class AndroidDeviceDispatcher extends Dispatcher<AndroidDevice, channels.AndroidDeviceChannel, AndroidDispatcher, AndroidDeviceDispatcher> implements channels.AndroidDeviceChannel { export class AndroidDeviceDispatcher extends Dispatcher<AndroidDevice, channels.AndroidDeviceChannel, AndroidDispatcher> implements channels.AndroidDeviceChannel {
_type_EventTarget = true; _type_EventTarget = true;
_type_AndroidDevice = true; _type_AndroidDevice = true;
@ -53,7 +53,7 @@ export class AndroidDeviceDispatcher extends Dispatcher<AndroidDevice, channels.
super(scope, device, 'AndroidDevice', { super(scope, device, 'AndroidDevice', {
model: device.model, model: device.model,
serial: device.serial, serial: device.serial,
}, true); });
for (const webView of device.webViews()) for (const webView of device.webViews())
this._dispatchEvent('webViewAdded', { webView }); this._dispatchEvent('webViewAdded', { webView });
this.addObjectListener(AndroidDevice.Events.WebViewAdded, webView => this._dispatchEvent('webViewAdded', { webView })); this.addObjectListener(AndroidDevice.Events.WebViewAdded, webView => this._dispatchEvent('webViewAdded', { webView }));
@ -145,7 +145,7 @@ export class AndroidDeviceDispatcher extends Dispatcher<AndroidDevice, channels.
async open(params: channels.AndroidDeviceOpenParams, metadata: CallMetadata): Promise<channels.AndroidDeviceOpenResult> { async open(params: channels.AndroidDeviceOpenParams, metadata: CallMetadata): Promise<channels.AndroidDeviceOpenResult> {
const socket = await this._object.open(params.command); const socket = await this._object.open(params.command);
return { socket: new AndroidSocketDispatcher(this._scope, socket) }; return { socket: new AndroidSocketDispatcher(this, socket) };
} }
async installApk(params: channels.AndroidDeviceInstallApkParams) { async installApk(params: channels.AndroidDeviceInstallApkParams) {
@ -158,7 +158,7 @@ export class AndroidDeviceDispatcher extends Dispatcher<AndroidDevice, channels.
async launchBrowser(params: channels.AndroidDeviceLaunchBrowserParams): Promise<channels.AndroidDeviceLaunchBrowserResult> { async launchBrowser(params: channels.AndroidDeviceLaunchBrowserParams): Promise<channels.AndroidDeviceLaunchBrowserResult> {
const context = await this._object.launchBrowser(params.pkg, params); const context = await this._object.launchBrowser(params.pkg, params);
return { context: new BrowserContextDispatcher(this._scope, context) }; return { context: new BrowserContextDispatcher(this, context) };
} }
async close(params: channels.AndroidDeviceCloseParams) { async close(params: channels.AndroidDeviceCloseParams) {
@ -170,15 +170,15 @@ export class AndroidDeviceDispatcher extends Dispatcher<AndroidDevice, channels.
} }
async connectToWebView(params: channels.AndroidDeviceConnectToWebViewParams): Promise<channels.AndroidDeviceConnectToWebViewResult> { async connectToWebView(params: channels.AndroidDeviceConnectToWebViewParams): Promise<channels.AndroidDeviceConnectToWebViewResult> {
return { context: new BrowserContextDispatcher(this._scope, await this._object.connectToWebView(params.socketName)) }; return { context: new BrowserContextDispatcher(this, await this._object.connectToWebView(params.socketName)) };
} }
} }
export class AndroidSocketDispatcher extends Dispatcher<SocketBackend, channels.AndroidSocketChannel, AndroidDeviceDispatcher, AndroidSocketDispatcher> implements channels.AndroidSocketChannel { export class AndroidSocketDispatcher extends Dispatcher<SocketBackend, channels.AndroidSocketChannel, AndroidDeviceDispatcher> implements channels.AndroidSocketChannel {
_type_AndroidSocket = true; _type_AndroidSocket = true;
constructor(scope: AndroidDeviceDispatcher, socket: SocketBackend) { constructor(scope: AndroidDeviceDispatcher, socket: SocketBackend) {
super(scope, socket, 'AndroidSocket', {}, true); super(scope, socket, 'AndroidSocket', {});
this.addObjectListener('data', (data: Buffer) => this._dispatchEvent('data', { data })); this.addObjectListener('data', (data: Buffer) => this._dispatchEvent('data', { data }));
this.addObjectListener('close', () => { this.addObjectListener('close', () => {
this._dispatchEvent('close'); this._dispatchEvent('close');

View file

@ -62,7 +62,7 @@ export class ArtifactDispatcher extends Dispatcher<Artifact, channels.ArtifactCh
} }
try { try {
const readable = fs.createReadStream(localPath); const readable = fs.createReadStream(localPath);
const stream = new StreamDispatcher(this._scope, readable); const stream = new StreamDispatcher(this, 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 Artifact until the stream is consumed. // Block the Artifact until the stream is consumed.
@ -83,7 +83,7 @@ export class ArtifactDispatcher extends Dispatcher<Artifact, channels.ArtifactCh
if (!fileName) if (!fileName)
return {}; return {};
const readable = fs.createReadStream(fileName); const readable = fs.createReadStream(fileName);
return { stream: new StreamDispatcher(this._scope, readable) }; return { stream: new StreamDispatcher(this, readable) };
} }
async failure(): Promise<channels.ArtifactFailureResult> { async failure(): Promise<channels.ArtifactFailureResult> {

View file

@ -34,7 +34,7 @@ import * as path from 'path';
import { createGuid } from '../../utils'; import { createGuid } from '../../utils';
import { WritableStreamDispatcher } from './writableStreamDispatcher'; import { WritableStreamDispatcher } from './writableStreamDispatcher';
export class BrowserContextDispatcher extends Dispatcher<BrowserContext, channels.BrowserContextChannel, DispatcherScope, BrowserContextDispatcher> implements channels.BrowserContextChannel { export class BrowserContextDispatcher extends Dispatcher<BrowserContext, channels.BrowserContextChannel, DispatcherScope> implements channels.BrowserContextChannel {
_type_EventTarget = true; _type_EventTarget = true;
_type_BrowserContext = true; _type_BrowserContext = true;
private _context: BrowserContext; private _context: BrowserContext;
@ -48,7 +48,7 @@ export class BrowserContextDispatcher extends Dispatcher<BrowserContext, channel
isChromium: context._browser.options.isChromium, isChromium: context._browser.options.isChromium,
requestContext, requestContext,
tracing, tracing,
}, true); });
this.adopt(requestContext); this.adopt(requestContext);
this.adopt(tracing); this.adopt(tracing);
@ -70,9 +70,9 @@ export class BrowserContextDispatcher extends Dispatcher<BrowserContext, channel
} }
for (const page of context.pages()) for (const page of context.pages())
this._dispatchEvent('page', { page: PageDispatcher.from(this._scope, page) }); this._dispatchEvent('page', { page: PageDispatcher.from(this, page) });
this.addObjectListener(BrowserContext.Events.Page, page => { this.addObjectListener(BrowserContext.Events.Page, page => {
this._dispatchEvent('page', { page: PageDispatcher.from(this._scope, page) }); this._dispatchEvent('page', { page: PageDispatcher.from(this, page) });
}); });
this.addObjectListener(BrowserContext.Events.Close, () => { this.addObjectListener(BrowserContext.Events.Close, () => {
this._dispatchEvent('close'); this._dispatchEvent('close');
@ -81,33 +81,33 @@ export class BrowserContextDispatcher extends Dispatcher<BrowserContext, channel
if (context._browser.options.name === 'chromium') { if (context._browser.options.name === 'chromium') {
for (const page of (context as CRBrowserContext).backgroundPages()) for (const page of (context as CRBrowserContext).backgroundPages())
this._dispatchEvent('backgroundPage', { page: PageDispatcher.from(this._scope, page) }); this._dispatchEvent('backgroundPage', { page: PageDispatcher.from(this, page) });
this.addObjectListener(CRBrowserContext.CREvents.BackgroundPage, page => this._dispatchEvent('backgroundPage', { page: PageDispatcher.from(this._scope, page) })); this.addObjectListener(CRBrowserContext.CREvents.BackgroundPage, page => this._dispatchEvent('backgroundPage', { page: PageDispatcher.from(this, page) }));
for (const serviceWorker of (context as CRBrowserContext).serviceWorkers()) for (const serviceWorker of (context as CRBrowserContext).serviceWorkers())
this._dispatchEvent('serviceWorker', { worker: new WorkerDispatcher(this._scope, serviceWorker) }); this._dispatchEvent('serviceWorker', { worker: new WorkerDispatcher(this, serviceWorker) });
this.addObjectListener(CRBrowserContext.CREvents.ServiceWorker, serviceWorker => this._dispatchEvent('serviceWorker', { worker: new WorkerDispatcher(this._scope, serviceWorker) })); this.addObjectListener(CRBrowserContext.CREvents.ServiceWorker, serviceWorker => this._dispatchEvent('serviceWorker', { worker: new WorkerDispatcher(this, serviceWorker) }));
} }
this.addObjectListener(BrowserContext.Events.Request, (request: Request) => { this.addObjectListener(BrowserContext.Events.Request, (request: Request) => {
return this._dispatchEvent('request', { return this._dispatchEvent('request', {
request: RequestDispatcher.from(this._scope, request), request: RequestDispatcher.from(this, request),
page: PageDispatcher.fromNullable(this._scope, request.frame()?._page.initializedOrUndefined()) page: PageDispatcher.fromNullable(this, request.frame()?._page.initializedOrUndefined())
}); });
}); });
this.addObjectListener(BrowserContext.Events.Response, (response: Response) => this._dispatchEvent('response', { this.addObjectListener(BrowserContext.Events.Response, (response: Response) => this._dispatchEvent('response', {
response: ResponseDispatcher.from(this._scope, response), response: ResponseDispatcher.from(this, response),
page: PageDispatcher.fromNullable(this._scope, response.frame()?._page.initializedOrUndefined()) page: PageDispatcher.fromNullable(this, response.frame()?._page.initializedOrUndefined())
})); }));
this.addObjectListener(BrowserContext.Events.RequestFailed, (request: Request) => this._dispatchEvent('requestFailed', { this.addObjectListener(BrowserContext.Events.RequestFailed, (request: Request) => this._dispatchEvent('requestFailed', {
request: RequestDispatcher.from(this._scope, request), request: RequestDispatcher.from(this, request),
failureText: request._failureText || undefined, failureText: request._failureText || undefined,
responseEndTiming: request._responseEndTiming, responseEndTiming: request._responseEndTiming,
page: PageDispatcher.fromNullable(this._scope, request.frame()?._page.initializedOrUndefined()) page: PageDispatcher.fromNullable(this, request.frame()?._page.initializedOrUndefined())
})); }));
this.addObjectListener(BrowserContext.Events.RequestFinished, ({ request, response }: { request: Request, response: Response | null }) => this._dispatchEvent('requestFinished', { this.addObjectListener(BrowserContext.Events.RequestFinished, ({ request, response }: { request: Request, response: Response | null }) => this._dispatchEvent('requestFinished', {
request: RequestDispatcher.from(this._scope, request), request: RequestDispatcher.from(this, request),
response: ResponseDispatcher.fromNullable(this._scope, response), response: ResponseDispatcher.fromNullable(this, response),
responseEndTiming: request._responseEndTiming, responseEndTiming: request._responseEndTiming,
page: PageDispatcher.fromNullable(this._scope, request.frame()?._page.initializedOrUndefined()), page: PageDispatcher.fromNullable(this, request.frame()?._page.initializedOrUndefined()),
})); }));
} }
@ -117,7 +117,7 @@ export class BrowserContextDispatcher extends Dispatcher<BrowserContext, channel
await fs.promises.mkdir(tmpDir); await fs.promises.mkdir(tmpDir);
this._context._tempDirs.push(tmpDir); this._context._tempDirs.push(tmpDir);
const file = fs.createWriteStream(path.join(tmpDir, params.name)); const file = fs.createWriteStream(path.join(tmpDir, params.name));
return { writableStream: new WritableStreamDispatcher(this._scope, file) }; return { writableStream: new WritableStreamDispatcher(this, file) };
} }
async setDefaultNavigationTimeoutNoReply(params: channels.BrowserContextSetDefaultNavigationTimeoutNoReplyParams) { async setDefaultNavigationTimeoutNoReply(params: channels.BrowserContextSetDefaultNavigationTimeoutNoReplyParams) {
@ -130,7 +130,7 @@ export class BrowserContextDispatcher extends Dispatcher<BrowserContext, channel
async exposeBinding(params: channels.BrowserContextExposeBindingParams): Promise<void> { async exposeBinding(params: channels.BrowserContextExposeBindingParams): Promise<void> {
await this._context.exposeBinding(params.name, !!params.needsHandle, (source, ...args) => { await this._context.exposeBinding(params.name, !!params.needsHandle, (source, ...args) => {
const pageDispatcher = PageDispatcher.from(this._scope, source.page); const pageDispatcher = PageDispatcher.from(this, source.page);
const binding = new BindingCallDispatcher(pageDispatcher, params.name, !!params.needsHandle, source, args); const binding = new BindingCallDispatcher(pageDispatcher, params.name, !!params.needsHandle, source, args);
this._dispatchEvent('bindingCall', { binding }); this._dispatchEvent('bindingCall', { binding });
return binding.promise(); return binding.promise();
@ -187,7 +187,7 @@ export class BrowserContextDispatcher extends Dispatcher<BrowserContext, channel
return; return;
} }
await this._context.setRequestInterceptor((route, request) => { await this._context.setRequestInterceptor((route, request) => {
this._dispatchEvent('route', { route: RouteDispatcher.from(RequestDispatcher.from(this._scope, request), route) }); this._dispatchEvent('route', { route: RouteDispatcher.from(RequestDispatcher.from(this, request), route) });
}); });
} }
@ -213,7 +213,7 @@ export class BrowserContextDispatcher extends Dispatcher<BrowserContext, channel
if (!params.page && !params.frame || params.page && params.frame) if (!params.page && !params.frame || params.page && params.frame)
throw new Error(`CDP session must be initiated with either Page or Frame, not none or both`); throw new Error(`CDP session must be initiated with either Page or Frame, not none or both`);
const crBrowserContext = this._object as CRBrowserContext; const crBrowserContext = this._object as CRBrowserContext;
return { session: new CDPSessionDispatcher(this._scope, await crBrowserContext.newCDPSession((params.page ? params.page as PageDispatcher : params.frame as FrameDispatcher)._object)) }; return { session: new CDPSessionDispatcher(this, await crBrowserContext.newCDPSession((params.page ? params.page as PageDispatcher : params.frame as FrameDispatcher)._object)) };
} }
async harStart(params: channels.BrowserContextHarStartParams): Promise<channels.BrowserContextHarStartResult> { async harStart(params: channels.BrowserContextHarStartParams): Promise<channels.BrowserContextHarStartResult> {
@ -225,7 +225,7 @@ export class BrowserContextDispatcher extends Dispatcher<BrowserContext, channel
const artifact = await this._context._harExport(params.harId); const artifact = await this._context._harExport(params.harId);
if (!artifact) if (!artifact)
throw new Error('No HAR artifact. Ensure record.harPath is set.'); throw new Error('No HAR artifact. Ensure record.harPath is set.');
return { artifact: new ArtifactDispatcher(this._scope, artifact) }; return { artifact: new ArtifactDispatcher(this, artifact) };
} }
override _dispose() { override _dispose() {

View file

@ -29,11 +29,11 @@ import { BrowserContext } from '../browserContext';
import { Selectors } from '../selectors'; import { Selectors } from '../selectors';
import type { BrowserTypeDispatcher } from './browserTypeDispatcher'; import type { BrowserTypeDispatcher } from './browserTypeDispatcher';
export class BrowserDispatcher extends Dispatcher<Browser, channels.BrowserChannel, BrowserTypeDispatcher, BrowserDispatcher> implements channels.BrowserChannel { export class BrowserDispatcher extends Dispatcher<Browser, channels.BrowserChannel, BrowserTypeDispatcher> implements channels.BrowserChannel {
_type_Browser = true; _type_Browser = true;
constructor(scope: BrowserTypeDispatcher, browser: Browser) { constructor(scope: BrowserTypeDispatcher, browser: Browser) {
super(scope, browser, 'Browser', { version: browser.version(), name: browser.options.name }, true); super(scope, browser, 'Browser', { version: browser.version(), name: browser.options.name });
this.addObjectListener(Browser.Events.Disconnected, () => this._didClose()); this.addObjectListener(Browser.Events.Disconnected, () => this._didClose());
} }
@ -44,11 +44,11 @@ export class BrowserDispatcher extends Dispatcher<Browser, channels.BrowserChann
async newContext(params: channels.BrowserNewContextParams, metadata: CallMetadata): Promise<channels.BrowserNewContextResult> { async newContext(params: channels.BrowserNewContextParams, metadata: CallMetadata): Promise<channels.BrowserNewContextResult> {
const context = await this._object.newContext(metadata, params); const context = await this._object.newContext(metadata, params);
return { context: new BrowserContextDispatcher(this._scope, context) }; return { context: new BrowserContextDispatcher(this, context) };
} }
async newContextForReuse(params: channels.BrowserNewContextForReuseParams, metadata: CallMetadata): Promise<channels.BrowserNewContextForReuseResult> { async newContextForReuse(params: channels.BrowserNewContextForReuseParams, metadata: CallMetadata): Promise<channels.BrowserNewContextForReuseResult> {
return newContextForReuse(this._object, this._scope, params, null, metadata); return newContextForReuse(this._object, this, params, null, metadata);
} }
async close(): Promise<void> { async close(): Promise<void> {
@ -63,7 +63,7 @@ export class BrowserDispatcher extends Dispatcher<Browser, channels.BrowserChann
if (!this._object.options.isChromium) if (!this._object.options.isChromium)
throw new Error(`CDP session is only available in Chromium`); throw new Error(`CDP session is only available in Chromium`);
const crBrowser = this._object as CRBrowser; const crBrowser = this._object as CRBrowser;
return { session: new CDPSessionDispatcher(this._scope, await crBrowser.newBrowserCDPSession()) }; return { session: new CDPSessionDispatcher(this, await crBrowser.newBrowserCDPSession()) };
} }
async startTracing(params: channels.BrowserStartTracingParams): Promise<void> { async startTracing(params: channels.BrowserStartTracingParams): Promise<void> {
@ -82,13 +82,13 @@ export class BrowserDispatcher extends Dispatcher<Browser, channels.BrowserChann
} }
// This class implements multiplexing browser dispatchers over a single Browser instance. // This class implements multiplexing browser dispatchers over a single Browser instance.
export class ConnectedBrowserDispatcher extends Dispatcher<Browser, channels.BrowserChannel, RootDispatcher, BrowserDispatcher> implements channels.BrowserChannel { export class ConnectedBrowserDispatcher extends Dispatcher<Browser, channels.BrowserChannel, RootDispatcher> implements channels.BrowserChannel {
_type_Browser = true; _type_Browser = true;
private _contexts = new Set<BrowserContext>(); private _contexts = new Set<BrowserContext>();
readonly selectors: Selectors; readonly selectors: Selectors;
constructor(scope: RootDispatcher, browser: Browser) { constructor(scope: RootDispatcher, browser: Browser) {
super(scope, browser, 'Browser', { version: browser.version(), name: browser.options.name }, true); super(scope, browser, 'Browser', { version: browser.version(), name: browser.options.name });
// When we have a remotely-connected browser, each client gets a fresh Selector instance, // When we have a remotely-connected browser, each client gets a fresh Selector instance,
// so that two clients do not interfere between each other. // so that two clients do not interfere between each other.
this.selectors = new Selectors(); this.selectors = new Selectors();
@ -101,11 +101,11 @@ export class ConnectedBrowserDispatcher extends Dispatcher<Browser, channels.Bro
this._contexts.add(context); this._contexts.add(context);
context.setSelectors(this.selectors); context.setSelectors(this.selectors);
context.on(BrowserContext.Events.Close, () => this._contexts.delete(context)); context.on(BrowserContext.Events.Close, () => this._contexts.delete(context));
return { context: new BrowserContextDispatcher(this._scope, context) }; return { context: new BrowserContextDispatcher(this, context) };
} }
async newContextForReuse(params: channels.BrowserNewContextForReuseParams, metadata: CallMetadata): Promise<channels.BrowserNewContextForReuseResult> { async newContextForReuse(params: channels.BrowserNewContextForReuseParams, metadata: CallMetadata): Promise<channels.BrowserNewContextForReuseResult> {
return newContextForReuse(this._object, this._scope, params, this.selectors, metadata); return newContextForReuse(this._object, this as any as BrowserDispatcher, params, this.selectors, metadata);
} }
async close(): Promise<void> { async close(): Promise<void> {
@ -120,7 +120,7 @@ export class ConnectedBrowserDispatcher extends Dispatcher<Browser, channels.Bro
if (!this._object.options.isChromium) if (!this._object.options.isChromium)
throw new Error(`CDP session is only available in Chromium`); throw new Error(`CDP session is only available in Chromium`);
const crBrowser = this._object as CRBrowser; const crBrowser = this._object as CRBrowser;
return { session: new CDPSessionDispatcher(this._scope, await crBrowser.newBrowserCDPSession()) }; return { session: new CDPSessionDispatcher(this as any as BrowserDispatcher, await crBrowser.newBrowserCDPSession()) };
} }
async startTracing(params: channels.BrowserStartTracingParams): Promise<void> { async startTracing(params: channels.BrowserStartTracingParams): Promise<void> {

View file

@ -29,28 +29,28 @@ import { ProgressController } from '../progress';
import { WebSocketTransport } from '../transport'; import { WebSocketTransport } from '../transport';
import { findValidator, ValidationError, type ValidatorContext } from '../../protocol/validator'; import { findValidator, ValidationError, type ValidatorContext } from '../../protocol/validator';
export class BrowserTypeDispatcher extends Dispatcher<BrowserType, channels.BrowserTypeChannel, RootDispatcher, BrowserTypeDispatcher> implements channels.BrowserTypeChannel { export class BrowserTypeDispatcher extends Dispatcher<BrowserType, channels.BrowserTypeChannel, RootDispatcher> implements channels.BrowserTypeChannel {
_type_BrowserType = true; _type_BrowserType = true;
constructor(scope: RootDispatcher, browserType: BrowserType) { constructor(scope: RootDispatcher, browserType: BrowserType) {
super(scope, browserType, 'BrowserType', { super(scope, browserType, 'BrowserType', {
executablePath: browserType.executablePath(), executablePath: browserType.executablePath(),
name: browserType.name() name: browserType.name()
}, true); });
} }
async launch(params: channels.BrowserTypeLaunchParams, metadata: CallMetadata): Promise<channels.BrowserTypeLaunchResult> { async launch(params: channels.BrowserTypeLaunchParams, metadata: CallMetadata): Promise<channels.BrowserTypeLaunchResult> {
const browser = await this._object.launch(metadata, params); const browser = await this._object.launch(metadata, params);
return { browser: new BrowserDispatcher(this._scope, browser) }; return { browser: new BrowserDispatcher(this, browser) };
} }
async launchPersistentContext(params: channels.BrowserTypeLaunchPersistentContextParams, metadata: CallMetadata): Promise<channels.BrowserTypeLaunchPersistentContextResult> { async launchPersistentContext(params: channels.BrowserTypeLaunchPersistentContextParams, metadata: CallMetadata): Promise<channels.BrowserTypeLaunchPersistentContextResult> {
const browserContext = await this._object.launchPersistentContext(metadata, params.userDataDir, params); const browserContext = await this._object.launchPersistentContext(metadata, params.userDataDir, params);
return { context: new BrowserContextDispatcher(this._scope, browserContext) }; return { context: new BrowserContextDispatcher(this, browserContext) };
} }
async connectOverCDP(params: channels.BrowserTypeConnectOverCDPParams, metadata: CallMetadata): Promise<channels.BrowserTypeConnectOverCDPResult> { async connectOverCDP(params: channels.BrowserTypeConnectOverCDPParams, metadata: CallMetadata): Promise<channels.BrowserTypeConnectOverCDPResult> {
const browser = await this._object.connectOverCDP(metadata, params.endpointURL, params, params.timeout); const browser = await this._object.connectOverCDP(metadata, params.endpointURL, params, params.timeout);
const browserDispatcher = new BrowserDispatcher(this._scope, browser); const browserDispatcher = new BrowserDispatcher(this, browser);
return { return {
browser: browserDispatcher, browser: browserDispatcher,
defaultContext: browser._defaultContext ? new BrowserContextDispatcher(browserDispatcher, browser._defaultContext) : undefined, defaultContext: browser._defaultContext ? new BrowserContextDispatcher(browserDispatcher, browser._defaultContext) : undefined,
@ -64,7 +64,7 @@ export class BrowserTypeDispatcher extends Dispatcher<BrowserType, channels.Brow
const paramsHeaders = Object.assign({ 'User-Agent': getUserAgent() }, params.headers || {}); const paramsHeaders = Object.assign({ 'User-Agent': getUserAgent() }, params.headers || {});
const transport = await WebSocketTransport.connect(progress, params.wsEndpoint, paramsHeaders, true); const transport = await WebSocketTransport.connect(progress, params.wsEndpoint, paramsHeaders, true);
let socksInterceptor: SocksInterceptor | undefined; let socksInterceptor: SocksInterceptor | undefined;
const pipe = new JsonPipeDispatcher(this._scope); const pipe = new JsonPipeDispatcher(this);
transport.onmessage = json => { transport.onmessage = json => {
if (json.method === '__create__' && json.params.type === 'SocksSupport') if (json.method === '__create__' && json.params.type === 'SocksSupport')
socksInterceptor = new SocksInterceptor(transport, params.socksProxyRedirectPortForTest, json.params.guid); socksInterceptor = new SocksInterceptor(transport, params.socksProxyRedirectPortForTest, json.params.guid);

View file

@ -21,11 +21,11 @@ import { Dispatcher } from './dispatcher';
import type { BrowserDispatcher } from './browserDispatcher'; import type { BrowserDispatcher } from './browserDispatcher';
import type { BrowserContextDispatcher } from './browserContextDispatcher'; import type { BrowserContextDispatcher } from './browserContextDispatcher';
export class CDPSessionDispatcher extends Dispatcher<CRSession, channels.CDPSessionChannel, BrowserDispatcher | BrowserContextDispatcher, CDPSessionDispatcher> implements channels.CDPSessionChannel { export class CDPSessionDispatcher extends Dispatcher<CRSession, channels.CDPSessionChannel, BrowserDispatcher | BrowserContextDispatcher> implements channels.CDPSessionChannel {
_type_CDPSession = true; _type_CDPSession = true;
constructor(scope: BrowserDispatcher | BrowserContextDispatcher, crSession: CRSession) { constructor(scope: BrowserDispatcher | BrowserContextDispatcher, crSession: CRSession) {
super(scope, crSession, 'CDPSession', {}, true); super(scope, crSession, 'CDPSession', {});
crSession._eventListener = (method, params) => { crSession._eventListener = (method, params) => {
this._dispatchEvent('event', { method, params }); this._dispatchEvent('event', { method, params });
}; };

View file

@ -44,9 +44,8 @@ export function lookupNullableDispatcher<DispatcherType>(object: any | null): Di
return object ? lookupDispatcher(object) : undefined; return object ? lookupDispatcher(object) : undefined;
} }
export class Dispatcher<Type extends { guid: string }, ChannelType, ParentScopeType extends DispatcherScope, ScopeType extends DispatcherScope = ParentScopeType> extends EventEmitter implements channels.Channel { export class Dispatcher<Type extends { guid: string }, ChannelType, ParentScopeType extends DispatcherScope> extends EventEmitter implements channels.Channel {
private _connection: DispatcherConnection; private _connection: DispatcherConnection;
private _isScope: boolean;
// Parent is always "isScope". // Parent is always "isScope".
private _parent: ParentScopeType | undefined; private _parent: ParentScopeType | undefined;
// Only "isScope" channel owners have registered dispatchers inside. // Only "isScope" channel owners have registered dispatchers inside.
@ -56,16 +55,13 @@ export class Dispatcher<Type extends { guid: string }, ChannelType, ParentScopeT
readonly _guid: string; readonly _guid: string;
readonly _type: string; readonly _type: string;
readonly _scope: ScopeType;
_object: Type; _object: Type;
constructor(parent: ParentScopeType | DispatcherConnection, object: Type, type: string, initializer: channels.InitializerTraits<Type>, isScope?: boolean) { constructor(parent: ParentScopeType | DispatcherConnection, object: Type, type: string, initializer: channels.InitializerTraits<Type>) {
super(); super();
this._connection = parent instanceof DispatcherConnection ? parent : parent._connection; this._connection = parent instanceof DispatcherConnection ? parent : parent._connection;
this._isScope = !!isScope;
this._parent = parent instanceof DispatcherConnection ? undefined : parent; this._parent = parent instanceof DispatcherConnection ? undefined : parent;
this._scope = (isScope ? this : this._parent!) as any as ScopeType;
const guid = object.guid; const guid = object.guid;
assert(!this._connection._dispatchers.has(guid)); assert(!this._connection._dispatchers.has(guid));
@ -84,8 +80,8 @@ export class Dispatcher<Type extends { guid: string }, ChannelType, ParentScopeT
this._connection.sendCreate(this._parent, type, guid, initializer, this._parent._object); this._connection.sendCreate(this._parent, type, guid, initializer, this._parent._object);
} }
parentScope(): ParentScopeType | undefined { parentScope(): ParentScopeType {
return this._parent; return this._parent!;
} }
addObjectListener(eventName: (string | symbol), handler: (...args: any[]) => void) { addObjectListener(eventName: (string | symbol), handler: (...args: any[]) => void) {
@ -93,7 +89,6 @@ export class Dispatcher<Type extends { guid: string }, ChannelType, ParentScopeT
} }
adopt(child: DispatcherScope) { adopt(child: DispatcherScope) {
assert(this._isScope);
const oldParent = child._parent!; const oldParent = child._parent!;
oldParent._dispatchers.delete(child._guid); oldParent._dispatchers.delete(child._guid);
this._dispatchers.set(child._guid, child); this._dispatchers.set(child._guid, child);
@ -113,6 +108,11 @@ export class Dispatcher<Type extends { guid: string }, ChannelType, ParentScopeT
} }
_dispose() { _dispose() {
this._disposeRecursively();
this._connection.sendDispose(this);
}
private _disposeRecursively() {
assert(!this._disposed, `${this._guid} is disposed more than once`); assert(!this._disposed, `${this._guid} is disposed more than once`);
this._disposed = true; this._disposed = true;
eventsHelper.removeEventListeners(this._eventListeners); eventsHelper.removeEventListeners(this._eventListeners);
@ -124,11 +124,8 @@ export class Dispatcher<Type extends { guid: string }, ChannelType, ParentScopeT
// Dispose all children. // Dispose all children.
for (const dispatcher of [...this._dispatchers.values()]) for (const dispatcher of [...this._dispatchers.values()])
dispatcher._dispose(); dispatcher._disposeRecursively();
this._dispatchers.clear(); this._dispatchers.clear();
if (this._isScope)
this._connection.sendDispose(this);
delete (this._object as any)[dispatcherSymbol]; delete (this._object as any)[dispatcherSymbol];
} }
@ -150,7 +147,7 @@ export class RootDispatcher extends Dispatcher<{ guid: '' }, any, any> {
private _initialized = false; private _initialized = false;
constructor(connection: DispatcherConnection, private readonly createPlaywright?: (scope: RootDispatcher, options: channels.RootInitializeParams) => Promise<PlaywrightDispatcher>) { constructor(connection: DispatcherConnection, private readonly createPlaywright?: (scope: RootDispatcher, options: channels.RootInitializeParams) => Promise<PlaywrightDispatcher>) {
super(connection, { guid: '' }, 'Root', {}, true); super(connection, { guid: '' }, 'Root', {});
} }
async initialize(params: channels.RootInitializeParams): Promise<channels.RootInitializeResult> { async initialize(params: channels.RootInitializeParams): Promise<channels.RootInitializeResult> {

View file

@ -24,27 +24,27 @@ import type { PageDispatcher } from './pageDispatcher';
import { parseArgument, serializeResult } from './jsHandleDispatcher'; import { parseArgument, serializeResult } from './jsHandleDispatcher';
import { ElementHandleDispatcher } from './elementHandlerDispatcher'; import { ElementHandleDispatcher } from './elementHandlerDispatcher';
export class ElectronDispatcher extends Dispatcher<Electron, channels.ElectronChannel, RootDispatcher, ElectronDispatcher> implements channels.ElectronChannel { export class ElectronDispatcher extends Dispatcher<Electron, channels.ElectronChannel, RootDispatcher> implements channels.ElectronChannel {
_type_Electron = true; _type_Electron = true;
constructor(scope: RootDispatcher, electron: Electron) { constructor(scope: RootDispatcher, electron: Electron) {
super(scope, electron, 'Electron', {}, true); super(scope, electron, 'Electron', {});
} }
async launch(params: channels.ElectronLaunchParams): Promise<channels.ElectronLaunchResult> { async launch(params: channels.ElectronLaunchParams): Promise<channels.ElectronLaunchResult> {
const electronApplication = await this._object.launch(params); const electronApplication = await this._object.launch(params);
return { electronApplication: new ElectronApplicationDispatcher(this._scope, electronApplication) }; return { electronApplication: new ElectronApplicationDispatcher(this, electronApplication) };
} }
} }
export class ElectronApplicationDispatcher extends Dispatcher<ElectronApplication, channels.ElectronApplicationChannel, ElectronDispatcher, ElectronApplicationDispatcher> implements channels.ElectronApplicationChannel { export class ElectronApplicationDispatcher extends Dispatcher<ElectronApplication, channels.ElectronApplicationChannel, ElectronDispatcher> implements channels.ElectronApplicationChannel {
_type_EventTarget = true; _type_EventTarget = true;
_type_ElectronApplication = true; _type_ElectronApplication = true;
constructor(scope: ElectronDispatcher, electronApplication: ElectronApplication) { constructor(scope: ElectronDispatcher, electronApplication: ElectronApplication) {
super(scope, electronApplication, 'ElectronApplication', { super(scope, electronApplication, 'ElectronApplication', {
context: new BrowserContextDispatcher(scope, electronApplication.context()) context: new BrowserContextDispatcher(scope, electronApplication.context())
}, true); });
this.addObjectListener(ElectronApplication.Events.Close, () => { this.addObjectListener(ElectronApplication.Events.Close, () => {
this._dispatchEvent('close'); this._dispatchEvent('close');
this._dispose(); this._dispose();
@ -53,7 +53,7 @@ export class ElectronApplicationDispatcher extends Dispatcher<ElectronApplicatio
async browserWindow(params: channels.ElectronApplicationBrowserWindowParams): Promise<channels.ElectronApplicationBrowserWindowResult> { async browserWindow(params: channels.ElectronApplicationBrowserWindowParams): Promise<channels.ElectronApplicationBrowserWindowResult> {
const handle = await this._object.browserWindow((params.page as PageDispatcher).page()); const handle = await this._object.browserWindow((params.page as PageDispatcher).page());
return { handle: ElementHandleDispatcher.fromJSHandle(this._scope, handle) }; return { handle: ElementHandleDispatcher.fromJSHandle(this, handle) };
} }
async evaluateExpression(params: channels.ElectronApplicationEvaluateExpressionParams): Promise<channels.ElectronApplicationEvaluateExpressionResult> { async evaluateExpression(params: channels.ElectronApplicationEvaluateExpressionParams): Promise<channels.ElectronApplicationEvaluateExpressionResult> {
@ -64,7 +64,7 @@ export class ElectronApplicationDispatcher extends Dispatcher<ElectronApplicatio
async evaluateExpressionHandle(params: channels.ElectronApplicationEvaluateExpressionHandleParams): Promise<channels.ElectronApplicationEvaluateExpressionHandleResult> { async evaluateExpressionHandle(params: channels.ElectronApplicationEvaluateExpressionHandleParams): Promise<channels.ElectronApplicationEvaluateExpressionHandleResult> {
const handle = await this._object._nodeElectronHandlePromise; const handle = await this._object._nodeElectronHandlePromise;
const result = await handle.evaluateExpressionAndWaitForSignals(params.expression, params.isFunction, false /* returnByValue */, parseArgument(params.arg)); const result = await handle.evaluateExpressionAndWaitForSignals(params.expression, params.isFunction, false /* returnByValue */, parseArgument(params.arg));
return { handle: ElementHandleDispatcher.fromJSHandle(this._scope, result) }; return { handle: ElementHandleDispatcher.fromJSHandle(this, result) };
} }
async close(): Promise<void> { async close(): Promise<void> {

View file

@ -197,12 +197,12 @@ export class ElementHandleDispatcher extends JSHandleDispatcher implements chann
async querySelector(params: channels.ElementHandleQuerySelectorParams, metadata: CallMetadata): Promise<channels.ElementHandleQuerySelectorResult> { async querySelector(params: channels.ElementHandleQuerySelectorParams, metadata: CallMetadata): Promise<channels.ElementHandleQuerySelectorResult> {
const handle = await this._elementHandle.querySelector(params.selector, params); const handle = await this._elementHandle.querySelector(params.selector, params);
return { element: ElementHandleDispatcher.fromNullable(this._scope, handle) }; return { element: ElementHandleDispatcher.fromNullable(this.parentScope(), handle) };
} }
async querySelectorAll(params: channels.ElementHandleQuerySelectorAllParams, metadata: CallMetadata): Promise<channels.ElementHandleQuerySelectorAllResult> { async querySelectorAll(params: channels.ElementHandleQuerySelectorAllParams, metadata: CallMetadata): Promise<channels.ElementHandleQuerySelectorAllResult> {
const elements = await this._elementHandle.querySelectorAll(params.selector); const elements = await this._elementHandle.querySelectorAll(params.selector);
return { elements: elements.map(e => ElementHandleDispatcher.from(this._scope, e)) }; return { elements: elements.map(e => ElementHandleDispatcher.from(this.parentScope(), e)) };
} }
async evalOnSelector(params: channels.ElementHandleEvalOnSelectorParams, metadata: CallMetadata): Promise<channels.ElementHandleEvalOnSelectorResult> { async evalOnSelector(params: channels.ElementHandleEvalOnSelectorParams, metadata: CallMetadata): Promise<channels.ElementHandleEvalOnSelectorResult> {
@ -218,6 +218,6 @@ export class ElementHandleDispatcher extends JSHandleDispatcher implements chann
} }
async waitForSelector(params: channels.ElementHandleWaitForSelectorParams, metadata: CallMetadata): Promise<channels.ElementHandleWaitForSelectorResult> { async waitForSelector(params: channels.ElementHandleWaitForSelectorParams, metadata: CallMetadata): Promise<channels.ElementHandleWaitForSelectorResult> {
return { element: ElementHandleDispatcher.fromNullable(this._scope, await this._elementHandle.waitForSelector(metadata, params.selector, params)) }; return { element: ElementHandleDispatcher.fromNullable(this.parentScope(), await this._elementHandle.waitForSelector(metadata, params.selector, params)) };
} }
} }

View file

@ -49,7 +49,7 @@ export class FrameDispatcher extends Dispatcher<Frame, channels.FrameChannel, Pa
name: frame.name(), name: frame.name(),
parentFrame: FrameDispatcher.fromNullable(scope, frame.parentFrame()), parentFrame: FrameDispatcher.fromNullable(scope, frame.parentFrame()),
loadStates: Array.from(frame._subtreeLifecycleEvents), loadStates: Array.from(frame._subtreeLifecycleEvents),
}, true); });
this._frame = frame; this._frame = frame;
this.addObjectListener(Frame.Events.AddLifecycle, lifecycleEvent => { this.addObjectListener(Frame.Events.AddLifecycle, lifecycleEvent => {
this._dispatchEvent('loadstate', { add: lifecycleEvent }); this._dispatchEvent('loadstate', { add: lifecycleEvent });
@ -62,7 +62,7 @@ export class FrameDispatcher extends Dispatcher<Frame, channels.FrameChannel, Pa
return; return;
const params = { url: event.url, name: event.name, error: event.error ? event.error.message : undefined }; const params = { url: event.url, name: event.name, error: event.error ? event.error.message : undefined };
if (event.newDocument) if (event.newDocument)
(params as any).newDocument = { request: RequestDispatcher.fromNullable(scope.parentScope()!, event.newDocument.request || null) }; (params as any).newDocument = { request: RequestDispatcher.fromNullable(scope.parentScope(), event.newDocument.request || null) };
this._dispatchEvent('navigated', params); this._dispatchEvent('navigated', params);
}); });
} }
@ -72,7 +72,7 @@ export class FrameDispatcher extends Dispatcher<Frame, channels.FrameChannel, Pa
} }
async frameElement(): Promise<channels.FrameFrameElementResult> { async frameElement(): Promise<channels.FrameFrameElementResult> {
return { element: ElementHandleDispatcher.from(this._scope, await this._frame.frameElement()) }; return { element: ElementHandleDispatcher.from(this.parentScope(), await this._frame.frameElement()) };
} }
async evaluateExpression(params: channels.FrameEvaluateExpressionParams, metadata: CallMetadata): Promise<channels.FrameEvaluateExpressionResult> { async evaluateExpression(params: channels.FrameEvaluateExpressionParams, metadata: CallMetadata): Promise<channels.FrameEvaluateExpressionResult> {
@ -80,11 +80,11 @@ export class FrameDispatcher extends Dispatcher<Frame, channels.FrameChannel, Pa
} }
async evaluateExpressionHandle(params: channels.FrameEvaluateExpressionHandleParams, metadata: CallMetadata): Promise<channels.FrameEvaluateExpressionHandleResult> { async evaluateExpressionHandle(params: channels.FrameEvaluateExpressionHandleParams, metadata: CallMetadata): Promise<channels.FrameEvaluateExpressionHandleResult> {
return { handle: ElementHandleDispatcher.fromJSHandle(this._scope, await this._frame.evaluateExpressionHandleAndWaitForSignals(params.expression, params.isFunction, parseArgument(params.arg), 'main')) }; return { handle: ElementHandleDispatcher.fromJSHandle(this.parentScope(), await this._frame.evaluateExpressionHandleAndWaitForSignals(params.expression, params.isFunction, parseArgument(params.arg), 'main')) };
} }
async waitForSelector(params: channels.FrameWaitForSelectorParams, metadata: CallMetadata): Promise<channels.FrameWaitForSelectorResult> { async waitForSelector(params: channels.FrameWaitForSelectorParams, metadata: CallMetadata): Promise<channels.FrameWaitForSelectorResult> {
return { element: ElementHandleDispatcher.fromNullable(this._scope, await this._frame.waitForSelector(metadata, params.selector, params)) }; return { element: ElementHandleDispatcher.fromNullable(this.parentScope(), await this._frame.waitForSelector(metadata, params.selector, params)) };
} }
async dispatchEvent(params: channels.FrameDispatchEventParams, metadata: CallMetadata): Promise<void> { async dispatchEvent(params: channels.FrameDispatchEventParams, metadata: CallMetadata): Promise<void> {
@ -100,12 +100,12 @@ export class FrameDispatcher extends Dispatcher<Frame, channels.FrameChannel, Pa
} }
async querySelector(params: channels.FrameQuerySelectorParams, metadata: CallMetadata): Promise<channels.FrameQuerySelectorResult> { async querySelector(params: channels.FrameQuerySelectorParams, metadata: CallMetadata): Promise<channels.FrameQuerySelectorResult> {
return { element: ElementHandleDispatcher.fromNullable(this._scope, await this._frame.querySelector(params.selector, params)) }; return { element: ElementHandleDispatcher.fromNullable(this.parentScope(), await this._frame.querySelector(params.selector, params)) };
} }
async querySelectorAll(params: channels.FrameQuerySelectorAllParams, metadata: CallMetadata): Promise<channels.FrameQuerySelectorAllResult> { async querySelectorAll(params: channels.FrameQuerySelectorAllParams, metadata: CallMetadata): Promise<channels.FrameQuerySelectorAllResult> {
const elements = await this._frame.querySelectorAll(params.selector); const elements = await this._frame.querySelectorAll(params.selector);
return { elements: elements.map(e => ElementHandleDispatcher.from(this._scope, e)) }; return { elements: elements.map(e => ElementHandleDispatcher.from(this.parentScope(), e)) };
} }
async queryCount(params: channels.FrameQueryCountParams): Promise<channels.FrameQueryCountResult> { async queryCount(params: channels.FrameQueryCountParams): Promise<channels.FrameQueryCountResult> {
@ -121,11 +121,11 @@ export class FrameDispatcher extends Dispatcher<Frame, channels.FrameChannel, Pa
} }
async addScriptTag(params: channels.FrameAddScriptTagParams, metadata: CallMetadata): Promise<channels.FrameAddScriptTagResult> { async addScriptTag(params: channels.FrameAddScriptTagParams, metadata: CallMetadata): Promise<channels.FrameAddScriptTagResult> {
return { element: ElementHandleDispatcher.from(this._scope, await this._frame.addScriptTag(params)) }; return { element: ElementHandleDispatcher.from(this.parentScope(), await this._frame.addScriptTag(params)) };
} }
async addStyleTag(params: channels.FrameAddStyleTagParams, metadata: CallMetadata): Promise<channels.FrameAddStyleTagResult> { async addStyleTag(params: channels.FrameAddStyleTagParams, metadata: CallMetadata): Promise<channels.FrameAddStyleTagResult> {
return { element: ElementHandleDispatcher.from(this._scope, await this._frame.addStyleTag(params)) }; return { element: ElementHandleDispatcher.from(this.parentScope(), await this._frame.addStyleTag(params)) };
} }
async click(params: channels.FrameClickParams, metadata: CallMetadata): Promise<void> { async click(params: channels.FrameClickParams, metadata: CallMetadata): Promise<void> {
@ -245,7 +245,7 @@ export class FrameDispatcher extends Dispatcher<Frame, channels.FrameChannel, Pa
} }
async waitForFunction(params: channels.FrameWaitForFunctionParams, metadata: CallMetadata): Promise<channels.FrameWaitForFunctionResult> { async waitForFunction(params: channels.FrameWaitForFunctionParams, metadata: CallMetadata): Promise<channels.FrameWaitForFunctionResult> {
return { handle: ElementHandleDispatcher.fromJSHandle(this._scope, await this._frame._waitForFunctionExpression(metadata, params.expression, params.isFunction, parseArgument(params.arg), params)) }; return { handle: ElementHandleDispatcher.fromJSHandle(this.parentScope(), await this._frame._waitForFunctionExpression(metadata, params.expression, params.isFunction, parseArgument(params.arg), params)) };
} }
async title(params: channels.FrameTitleParams, metadata: CallMetadata): Promise<channels.FrameTitleResult> { async title(params: channels.FrameTitleParams, metadata: CallMetadata): Promise<channels.FrameTitleResult> {

View file

@ -41,19 +41,19 @@ export class JSHandleDispatcher extends Dispatcher<js.JSHandle, channels.JSHandl
async evaluateExpressionHandle(params: channels.JSHandleEvaluateExpressionHandleParams): Promise<channels.JSHandleEvaluateExpressionHandleResult> { async evaluateExpressionHandle(params: channels.JSHandleEvaluateExpressionHandleParams): Promise<channels.JSHandleEvaluateExpressionHandleResult> {
const jsHandle = await this._object.evaluateExpressionAndWaitForSignals(params.expression, params.isFunction, false /* returnByValue */, parseArgument(params.arg)); const jsHandle = await this._object.evaluateExpressionAndWaitForSignals(params.expression, params.isFunction, false /* returnByValue */, parseArgument(params.arg));
return { handle: ElementHandleDispatcher.fromJSHandle(this._scope, jsHandle) }; return { handle: ElementHandleDispatcher.fromJSHandle(this.parentScope(), jsHandle) };
} }
async getProperty(params: channels.JSHandleGetPropertyParams): Promise<channels.JSHandleGetPropertyResult> { async getProperty(params: channels.JSHandleGetPropertyParams): Promise<channels.JSHandleGetPropertyResult> {
const jsHandle = await this._object.getProperty(params.name); const jsHandle = await this._object.getProperty(params.name);
return { handle: ElementHandleDispatcher.fromJSHandle(this._scope, jsHandle) }; return { handle: ElementHandleDispatcher.fromJSHandle(this.parentScope(), jsHandle) };
} }
async getPropertyList(): Promise<channels.JSHandleGetPropertyListResult> { async getPropertyList(): Promise<channels.JSHandleGetPropertyListResult> {
const map = await this._object.getProperties(); const map = await this._object.getProperties();
const properties = []; const properties = [];
for (const [name, value] of map) for (const [name, value] of map)
properties.push({ name, value: ElementHandleDispatcher.fromJSHandle(this._scope, value) }); properties.push({ name, value: ElementHandleDispatcher.fromJSHandle(this.parentScope(), value) });
return { properties }; return { properties };
} }

View file

@ -164,7 +164,7 @@ export class WebSocketDispatcher extends Dispatcher<WebSocket, channels.WebSocke
} }
} }
export class APIRequestContextDispatcher extends Dispatcher<APIRequestContext, channels.APIRequestContextChannel, RootDispatcher | BrowserContextDispatcher, APIRequestContextDispatcher> implements channels.APIRequestContextChannel { export class APIRequestContextDispatcher extends Dispatcher<APIRequestContext, channels.APIRequestContextChannel, RootDispatcher | BrowserContextDispatcher> implements channels.APIRequestContextChannel {
_type_APIRequestContext = true; _type_APIRequestContext = true;
static from(scope: RootDispatcher | BrowserContextDispatcher, request: APIRequestContext): APIRequestContextDispatcher { static from(scope: RootDispatcher | BrowserContextDispatcher, request: APIRequestContext): APIRequestContextDispatcher {
@ -182,7 +182,7 @@ export class APIRequestContextDispatcher extends Dispatcher<APIRequestContext, c
super(parentScope, request, 'APIRequestContext', { super(parentScope, request, 'APIRequestContext', {
tracing, tracing,
}, true); });
this.adopt(tracing); this.adopt(tracing);
} }

View file

@ -38,7 +38,7 @@ import type { Download } from '../download';
import { createGuid } from '../../utils'; import { createGuid } from '../../utils';
import type { BrowserContextDispatcher } from './browserContextDispatcher'; import type { BrowserContextDispatcher } from './browserContextDispatcher';
export class PageDispatcher extends Dispatcher<Page, channels.PageChannel, BrowserContextDispatcher, PageDispatcher> implements channels.PageChannel { export class PageDispatcher extends Dispatcher<Page, channels.PageChannel, BrowserContextDispatcher> implements channels.PageChannel {
_type_EventTarget = true; _type_EventTarget = true;
_type_Page = true; _type_Page = true;
private _page: Page; private _page: Page;
@ -66,7 +66,7 @@ export class PageDispatcher extends Dispatcher<Page, channels.PageChannel, Brows
viewportSize: page.viewportSize() || undefined, viewportSize: page.viewportSize() || undefined,
isClosed: page.isClosed(), isClosed: page.isClosed(),
opener: PageDispatcher.fromNullable(parentScope, page.opener()) opener: PageDispatcher.fromNullable(parentScope, page.opener())
}, true); });
this.adopt(mainFrame); this.adopt(mainFrame);
@ -75,22 +75,22 @@ export class PageDispatcher extends Dispatcher<Page, channels.PageChannel, Brows
this._dispatchEvent('close'); this._dispatchEvent('close');
this._dispose(); this._dispose();
}); });
this.addObjectListener(Page.Events.Console, message => this._dispatchEvent('console', { message: new ConsoleMessageDispatcher(this._scope, message) })); this.addObjectListener(Page.Events.Console, message => this._dispatchEvent('console', { message: new ConsoleMessageDispatcher(this, message) }));
this.addObjectListener(Page.Events.Crash, () => this._dispatchEvent('crash')); this.addObjectListener(Page.Events.Crash, () => this._dispatchEvent('crash'));
this.addObjectListener(Page.Events.Dialog, dialog => this._dispatchEvent('dialog', { dialog: new DialogDispatcher(this._scope, dialog) })); this.addObjectListener(Page.Events.Dialog, dialog => this._dispatchEvent('dialog', { dialog: new DialogDispatcher(this, dialog) }));
this.addObjectListener(Page.Events.Download, (download: Download) => { this.addObjectListener(Page.Events.Download, (download: Download) => {
// Artifact can outlive the page, so bind to the context scope. // Artifact can outlive the page, so bind to the context scope.
this._dispatchEvent('download', { url: download.url, suggestedFilename: download.suggestedFilename(), artifact: new ArtifactDispatcher(parentScope, download.artifact) }); this._dispatchEvent('download', { url: download.url, suggestedFilename: download.suggestedFilename(), artifact: new ArtifactDispatcher(parentScope, download.artifact) });
}); });
this.addObjectListener(Page.Events.FileChooser, (fileChooser: FileChooser) => this._dispatchEvent('fileChooser', { this.addObjectListener(Page.Events.FileChooser, (fileChooser: FileChooser) => this._dispatchEvent('fileChooser', {
element: ElementHandleDispatcher.from(this._scope, fileChooser.element()), element: ElementHandleDispatcher.from(this, fileChooser.element()),
isMultiple: fileChooser.isMultiple() isMultiple: fileChooser.isMultiple()
})); }));
this.addObjectListener(Page.Events.FrameAttached, frame => this._onFrameAttached(frame)); this.addObjectListener(Page.Events.FrameAttached, frame => this._onFrameAttached(frame));
this.addObjectListener(Page.Events.FrameDetached, frame => this._onFrameDetached(frame)); this.addObjectListener(Page.Events.FrameDetached, frame => this._onFrameDetached(frame));
this.addObjectListener(Page.Events.PageError, error => this._dispatchEvent('pageError', { error: serializeError(error) })); this.addObjectListener(Page.Events.PageError, error => this._dispatchEvent('pageError', { error: serializeError(error) }));
this.addObjectListener(Page.Events.WebSocket, webSocket => this._dispatchEvent('webSocket', { webSocket: new WebSocketDispatcher(this._scope, webSocket) })); this.addObjectListener(Page.Events.WebSocket, webSocket => this._dispatchEvent('webSocket', { webSocket: new WebSocketDispatcher(this, webSocket) }));
this.addObjectListener(Page.Events.Worker, worker => this._dispatchEvent('worker', { worker: new WorkerDispatcher(this._scope, worker) })); this.addObjectListener(Page.Events.Worker, worker => this._dispatchEvent('worker', { worker: new WorkerDispatcher(this, worker) }));
this.addObjectListener(Page.Events.Video, (artifact: Artifact) => this._dispatchEvent('video', { artifact: existingDispatcher<ArtifactDispatcher>(artifact) })); this.addObjectListener(Page.Events.Video, (artifact: Artifact) => this._dispatchEvent('video', { artifact: existingDispatcher<ArtifactDispatcher>(artifact) }));
if (page._video) if (page._video)
this._dispatchEvent('video', { artifact: existingDispatcher<ArtifactDispatcher>(page._video) }); this._dispatchEvent('video', { artifact: existingDispatcher<ArtifactDispatcher>(page._video) });
@ -114,7 +114,7 @@ export class PageDispatcher extends Dispatcher<Page, channels.PageChannel, Brows
async exposeBinding(params: channels.PageExposeBindingParams, metadata: CallMetadata): Promise<void> { async exposeBinding(params: channels.PageExposeBindingParams, metadata: CallMetadata): Promise<void> {
await this._page.exposeBinding(params.name, !!params.needsHandle, (source, ...args) => { await this._page.exposeBinding(params.name, !!params.needsHandle, (source, ...args) => {
const binding = new BindingCallDispatcher(this._scope, params.name, !!params.needsHandle, source, args); const binding = new BindingCallDispatcher(this, params.name, !!params.needsHandle, source, args);
this._dispatchEvent('bindingCall', { binding }); this._dispatchEvent('bindingCall', { binding });
return binding.promise(); return binding.promise();
}); });
@ -159,7 +159,7 @@ export class PageDispatcher extends Dispatcher<Page, channels.PageChannel, Brows
return; return;
} }
await this._page.setClientRequestInterceptor((route, request) => { await this._page.setClientRequestInterceptor((route, request) => {
this._dispatchEvent('route', { route: RouteDispatcher.from(RequestDispatcher.from(this.parentScope()!, request), route) }); this._dispatchEvent('route', { route: RouteDispatcher.from(RequestDispatcher.from(this.parentScope(), request), route) });
}); });
} }
@ -282,7 +282,7 @@ export class PageDispatcher extends Dispatcher<Page, channels.PageChannel, Brows
} }
_onFrameAttached(frame: Frame) { _onFrameAttached(frame: Frame) {
this._dispatchEvent('frameAttached', { frame: FrameDispatcher.from(this._scope, frame) }); this._dispatchEvent('frameAttached', { frame: FrameDispatcher.from(this, frame) });
} }
_onFrameDetached(frame: Frame) { _onFrameDetached(frame: Frame) {
@ -296,7 +296,7 @@ export class PageDispatcher extends Dispatcher<Page, channels.PageChannel, Brows
} }
export class WorkerDispatcher extends Dispatcher<Worker, channels.WorkerChannel, PageDispatcher | BrowserContextDispatcher, WorkerDispatcher> implements channels.WorkerChannel { export class WorkerDispatcher extends Dispatcher<Worker, channels.WorkerChannel, PageDispatcher | BrowserContextDispatcher> implements channels.WorkerChannel {
_type_Worker = true; _type_Worker = true;
static fromNullable(scope: PageDispatcher | BrowserContextDispatcher, worker: Worker | null): WorkerDispatcher | undefined { static fromNullable(scope: PageDispatcher | BrowserContextDispatcher, worker: Worker | null): WorkerDispatcher | undefined {
@ -309,7 +309,7 @@ export class WorkerDispatcher extends Dispatcher<Worker, channels.WorkerChannel,
constructor(scope: PageDispatcher | BrowserContextDispatcher, worker: Worker) { constructor(scope: PageDispatcher | BrowserContextDispatcher, worker: Worker) {
super(scope, worker, 'Worker', { super(scope, worker, 'Worker', {
url: worker.url() url: worker.url()
}, true); });
this.addObjectListener(Worker.Events.Close, () => this._dispatchEvent('close')); this.addObjectListener(Worker.Events.Close, () => this._dispatchEvent('close'));
} }
@ -318,7 +318,7 @@ export class WorkerDispatcher extends Dispatcher<Worker, channels.WorkerChannel,
} }
async evaluateExpressionHandle(params: channels.WorkerEvaluateExpressionHandleParams, metadata: CallMetadata): Promise<channels.WorkerEvaluateExpressionHandleResult> { async evaluateExpressionHandle(params: channels.WorkerEvaluateExpressionHandleParams, metadata: CallMetadata): Promise<channels.WorkerEvaluateExpressionHandleResult> {
return { handle: ElementHandleDispatcher.fromJSHandle(this._scope, await this._object.evaluateExpressionHandle(params.expression, params.isFunction, parseArgument(params.arg))) }; return { handle: ElementHandleDispatcher.fromJSHandle(this, await this._object.evaluateExpressionHandle(params.expression, params.isFunction, parseArgument(params.arg))) };
} }
} }

View file

@ -52,14 +52,14 @@ export class PlaywrightDispatcher extends Dispatcher<Playwright, channels.Playwr
selectors: new SelectorsDispatcher(scope, browserDispatcher?.selectors || playwright.selectors), selectors: new SelectorsDispatcher(scope, browserDispatcher?.selectors || playwright.selectors),
preLaunchedBrowser: browserDispatcher, preLaunchedBrowser: browserDispatcher,
socksSupport: socksProxy ? new SocksSupportDispatcher(scope, socksProxy) : undefined, socksSupport: socksProxy ? new SocksSupportDispatcher(scope, socksProxy) : undefined,
}, false); });
this._type_Playwright = true; this._type_Playwright = true;
this._browserDispatcher = browserDispatcher; this._browserDispatcher = browserDispatcher;
} }
async newRequest(params: channels.PlaywrightNewRequestParams, metadata?: channels.Metadata): Promise<channels.PlaywrightNewRequestResult> { async newRequest(params: channels.PlaywrightNewRequestParams, metadata?: channels.Metadata): Promise<channels.PlaywrightNewRequestResult> {
const request = new GlobalAPIRequestContext(this._object, params); const request = new GlobalAPIRequestContext(this._object, params);
return { request: APIRequestContextDispatcher.from(this._scope, request) }; return { request: APIRequestContextDispatcher.from(this.parentScope(), request) };
} }
async hideHighlight(params: channels.PlaywrightHideHighlightParams, metadata?: channels.Metadata): Promise<channels.PlaywrightHideHighlightResult> { async hideHighlight(params: channels.PlaywrightHideHighlightParams, metadata?: channels.Metadata): Promise<channels.PlaywrightHideHighlightResult> {

View file

@ -15,16 +15,16 @@
*/ */
import type * as channels from '../../protocol/channels'; import type * as channels from '../../protocol/channels';
import type { DispatcherScope } from './dispatcher';
import { Dispatcher } from './dispatcher'; import { Dispatcher } from './dispatcher';
import type * as stream from 'stream'; import type * as stream from 'stream';
import { createGuid } from '../../utils'; import { createGuid } from '../../utils';
import type { ArtifactDispatcher } from './artifactDispatcher';
export class StreamDispatcher extends Dispatcher<{ guid: string, stream: stream.Readable }, channels.StreamChannel, DispatcherScope> implements channels.StreamChannel { export class StreamDispatcher extends Dispatcher<{ guid: string, stream: stream.Readable }, channels.StreamChannel, ArtifactDispatcher> implements channels.StreamChannel {
_type_Stream = true; _type_Stream = true;
private _ended: boolean = false; private _ended: boolean = false;
constructor(scope: DispatcherScope, stream: stream.Readable) { constructor(scope: ArtifactDispatcher, stream: stream.Readable) {
super(scope, { guid: 'stream@' + createGuid(), stream }, 'Stream', {}); super(scope, { guid: 'stream@' + createGuid(), stream }, 'Stream', {});
// In Node v12.9.0+ we can use readableEnded. // In Node v12.9.0+ we can use readableEnded.
stream.once('end', () => this._ended = true); stream.once('end', () => this._ended = true);

View file

@ -30,7 +30,7 @@ export class TracingDispatcher extends Dispatcher<Tracing, channels.TracingChann
} }
constructor(scope: BrowserContextDispatcher | APIRequestContextDispatcher, tracing: Tracing) { constructor(scope: BrowserContextDispatcher | APIRequestContextDispatcher, tracing: Tracing) {
super(scope, tracing, 'Tracing', {}, true); super(scope, tracing, 'Tracing', {});
} }
async tracingStart(params: channels.TracingTracingStartParams): Promise<channels.TracingTracingStartResult> { async tracingStart(params: channels.TracingTracingStartParams): Promise<channels.TracingTracingStartResult> {
@ -43,7 +43,7 @@ export class TracingDispatcher extends Dispatcher<Tracing, channels.TracingChann
async tracingStopChunk(params: channels.TracingTracingStopChunkParams): Promise<channels.TracingTracingStopChunkResult> { async tracingStopChunk(params: channels.TracingTracingStopChunkParams): Promise<channels.TracingTracingStopChunkResult> {
const { artifact, sourceEntries } = await this._object.stopChunk(params); const { artifact, sourceEntries } = await this._object.stopChunk(params);
return { artifact: artifact ? new ArtifactDispatcher(this._scope, artifact) : undefined, sourceEntries }; return { artifact: artifact ? new ArtifactDispatcher(this, artifact) : undefined, sourceEntries };
} }
async tracingStop(params: channels.TracingTracingStopParams): Promise<channels.TracingTracingStopResult> { async tracingStop(params: channels.TracingTracingStopParams): Promise<channels.TracingTracingStopResult> {