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 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;
constructor(scope: RootDispatcher, android: Android) {
super(scope, android, 'Android', {}, true);
super(scope, android, 'Android', {});
}
async devices(params: channels.AndroidDevicesParams): Promise<channels.AndroidDevicesResult> {
const devices = await this._object.devices(params);
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_AndroidDevice = true;
@ -53,7 +53,7 @@ export class AndroidDeviceDispatcher extends Dispatcher<AndroidDevice, channels.
super(scope, device, 'AndroidDevice', {
model: device.model,
serial: device.serial,
}, true);
});
for (const webView of device.webViews())
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> {
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) {
@ -158,7 +158,7 @@ export class AndroidDeviceDispatcher extends Dispatcher<AndroidDevice, channels.
async launchBrowser(params: channels.AndroidDeviceLaunchBrowserParams): Promise<channels.AndroidDeviceLaunchBrowserResult> {
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) {
@ -170,15 +170,15 @@ export class AndroidDeviceDispatcher extends Dispatcher<AndroidDevice, channels.
}
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;
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('close', () => {
this._dispatchEvent('close');

View File

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

View File

@ -34,7 +34,7 @@ import * as path from 'path';
import { createGuid } from '../../utils';
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_BrowserContext = true;
private _context: BrowserContext;
@ -48,7 +48,7 @@ export class BrowserContextDispatcher extends Dispatcher<BrowserContext, channel
isChromium: context._browser.options.isChromium,
requestContext,
tracing,
}, true);
});
this.adopt(requestContext);
this.adopt(tracing);
@ -70,9 +70,9 @@ export class BrowserContextDispatcher extends Dispatcher<BrowserContext, channel
}
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._dispatchEvent('page', { page: PageDispatcher.from(this._scope, page) });
this._dispatchEvent('page', { page: PageDispatcher.from(this, page) });
});
this.addObjectListener(BrowserContext.Events.Close, () => {
this._dispatchEvent('close');
@ -81,33 +81,33 @@ export class BrowserContextDispatcher extends Dispatcher<BrowserContext, channel
if (context._browser.options.name === 'chromium') {
for (const page of (context as CRBrowserContext).backgroundPages())
this._dispatchEvent('backgroundPage', { page: PageDispatcher.from(this._scope, page) });
this.addObjectListener(CRBrowserContext.CREvents.BackgroundPage, page => 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, page) }));
for (const serviceWorker of (context as CRBrowserContext).serviceWorkers())
this._dispatchEvent('serviceWorker', { worker: new WorkerDispatcher(this._scope, serviceWorker) });
this.addObjectListener(CRBrowserContext.CREvents.ServiceWorker, serviceWorker => 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, serviceWorker) }));
}
this.addObjectListener(BrowserContext.Events.Request, (request: Request) => {
return this._dispatchEvent('request', {
request: RequestDispatcher.from(this._scope, request),
page: PageDispatcher.fromNullable(this._scope, request.frame()?._page.initializedOrUndefined())
request: RequestDispatcher.from(this, request),
page: PageDispatcher.fromNullable(this, request.frame()?._page.initializedOrUndefined())
});
});
this.addObjectListener(BrowserContext.Events.Response, (response: Response) => this._dispatchEvent('response', {
response: ResponseDispatcher.from(this._scope, response),
page: PageDispatcher.fromNullable(this._scope, response.frame()?._page.initializedOrUndefined())
response: ResponseDispatcher.from(this, response),
page: PageDispatcher.fromNullable(this, response.frame()?._page.initializedOrUndefined())
}));
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,
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', {
request: RequestDispatcher.from(this._scope, request),
response: ResponseDispatcher.fromNullable(this._scope, response),
request: RequestDispatcher.from(this, request),
response: ResponseDispatcher.fromNullable(this, response),
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);
this._context._tempDirs.push(tmpDir);
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) {
@ -130,7 +130,7 @@ export class BrowserContextDispatcher extends Dispatcher<BrowserContext, channel
async exposeBinding(params: channels.BrowserContextExposeBindingParams): Promise<void> {
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);
this._dispatchEvent('bindingCall', { binding });
return binding.promise();
@ -187,7 +187,7 @@ export class BrowserContextDispatcher extends Dispatcher<BrowserContext, channel
return;
}
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)
throw new Error(`CDP session must be initiated with either Page or Frame, not none or both`);
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> {
@ -225,7 +225,7 @@ export class BrowserContextDispatcher extends Dispatcher<BrowserContext, channel
const artifact = await this._context._harExport(params.harId);
if (!artifact)
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() {

View File

@ -29,11 +29,11 @@ import { BrowserContext } from '../browserContext';
import { Selectors } from '../selectors';
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;
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());
}
@ -44,11 +44,11 @@ export class BrowserDispatcher extends Dispatcher<Browser, channels.BrowserChann
async newContext(params: channels.BrowserNewContextParams, metadata: CallMetadata): Promise<channels.BrowserNewContextResult> {
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> {
return newContextForReuse(this._object, this._scope, params, null, metadata);
return newContextForReuse(this._object, this, params, null, metadata);
}
async close(): Promise<void> {
@ -63,7 +63,7 @@ export class BrowserDispatcher extends Dispatcher<Browser, channels.BrowserChann
if (!this._object.options.isChromium)
throw new Error(`CDP session is only available in Chromium`);
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> {
@ -82,13 +82,13 @@ export class BrowserDispatcher extends Dispatcher<Browser, channels.BrowserChann
}
// 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;
private _contexts = new Set<BrowserContext>();
readonly selectors: Selectors;
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,
// so that two clients do not interfere between each other.
this.selectors = new Selectors();
@ -101,11 +101,11 @@ export class ConnectedBrowserDispatcher extends Dispatcher<Browser, channels.Bro
this._contexts.add(context);
context.setSelectors(this.selectors);
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> {
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> {
@ -120,7 +120,7 @@ export class ConnectedBrowserDispatcher extends Dispatcher<Browser, channels.Bro
if (!this._object.options.isChromium)
throw new Error(`CDP session is only available in Chromium`);
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> {

View File

@ -29,28 +29,28 @@ import { ProgressController } from '../progress';
import { WebSocketTransport } from '../transport';
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;
constructor(scope: RootDispatcher, browserType: BrowserType) {
super(scope, browserType, 'BrowserType', {
executablePath: browserType.executablePath(),
name: browserType.name()
}, true);
});
}
async launch(params: channels.BrowserTypeLaunchParams, metadata: CallMetadata): Promise<channels.BrowserTypeLaunchResult> {
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> {
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> {
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 {
browser: browserDispatcher,
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 transport = await WebSocketTransport.connect(progress, params.wsEndpoint, paramsHeaders, true);
let socksInterceptor: SocksInterceptor | undefined;
const pipe = new JsonPipeDispatcher(this._scope);
const pipe = new JsonPipeDispatcher(this);
transport.onmessage = json => {
if (json.method === '__create__' && json.params.type === 'SocksSupport')
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 { 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;
constructor(scope: BrowserDispatcher | BrowserContextDispatcher, crSession: CRSession) {
super(scope, crSession, 'CDPSession', {}, true);
super(scope, crSession, 'CDPSession', {});
crSession._eventListener = (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;
}
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 _isScope: boolean;
// Parent is always "isScope".
private _parent: ParentScopeType | undefined;
// 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 _type: string;
readonly _scope: ScopeType;
_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();
this._connection = parent instanceof DispatcherConnection ? parent : parent._connection;
this._isScope = !!isScope;
this._parent = parent instanceof DispatcherConnection ? undefined : parent;
this._scope = (isScope ? this : this._parent!) as any as ScopeType;
const guid = object.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);
}
parentScope(): ParentScopeType | undefined {
return this._parent;
parentScope(): ParentScopeType {
return this._parent!;
}
addObjectListener(eventName: (string | symbol), handler: (...args: any[]) => void) {
@ -93,7 +89,6 @@ export class Dispatcher<Type extends { guid: string }, ChannelType, ParentScopeT
}
adopt(child: DispatcherScope) {
assert(this._isScope);
const oldParent = child._parent!;
oldParent._dispatchers.delete(child._guid);
this._dispatchers.set(child._guid, child);
@ -113,6 +108,11 @@ export class Dispatcher<Type extends { guid: string }, ChannelType, ParentScopeT
}
_dispose() {
this._disposeRecursively();
this._connection.sendDispose(this);
}
private _disposeRecursively() {
assert(!this._disposed, `${this._guid} is disposed more than once`);
this._disposed = true;
eventsHelper.removeEventListeners(this._eventListeners);
@ -124,11 +124,8 @@ export class Dispatcher<Type extends { guid: string }, ChannelType, ParentScopeT
// Dispose all children.
for (const dispatcher of [...this._dispatchers.values()])
dispatcher._dispose();
dispatcher._disposeRecursively();
this._dispatchers.clear();
if (this._isScope)
this._connection.sendDispose(this);
delete (this._object as any)[dispatcherSymbol];
}
@ -150,7 +147,7 @@ export class RootDispatcher extends Dispatcher<{ guid: '' }, any, any> {
private _initialized = false;
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> {

View File

@ -24,27 +24,27 @@ import type { PageDispatcher } from './pageDispatcher';
import { parseArgument, serializeResult } from './jsHandleDispatcher';
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;
constructor(scope: RootDispatcher, electron: Electron) {
super(scope, electron, 'Electron', {}, true);
super(scope, electron, 'Electron', {});
}
async launch(params: channels.ElectronLaunchParams): Promise<channels.ElectronLaunchResult> {
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_ElectronApplication = true;
constructor(scope: ElectronDispatcher, electronApplication: ElectronApplication) {
super(scope, electronApplication, 'ElectronApplication', {
context: new BrowserContextDispatcher(scope, electronApplication.context())
}, true);
});
this.addObjectListener(ElectronApplication.Events.Close, () => {
this._dispatchEvent('close');
this._dispose();
@ -53,7 +53,7 @@ export class ElectronApplicationDispatcher extends Dispatcher<ElectronApplicatio
async browserWindow(params: channels.ElectronApplicationBrowserWindowParams): Promise<channels.ElectronApplicationBrowserWindowResult> {
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> {
@ -64,7 +64,7 @@ export class ElectronApplicationDispatcher extends Dispatcher<ElectronApplicatio
async evaluateExpressionHandle(params: channels.ElectronApplicationEvaluateExpressionHandleParams): Promise<channels.ElectronApplicationEvaluateExpressionHandleResult> {
const handle = await this._object._nodeElectronHandlePromise;
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> {

View File

@ -197,12 +197,12 @@ export class ElementHandleDispatcher extends JSHandleDispatcher implements chann
async querySelector(params: channels.ElementHandleQuerySelectorParams, metadata: CallMetadata): Promise<channels.ElementHandleQuerySelectorResult> {
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> {
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> {
@ -218,6 +218,6 @@ export class ElementHandleDispatcher extends JSHandleDispatcher implements chann
}
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(),
parentFrame: FrameDispatcher.fromNullable(scope, frame.parentFrame()),
loadStates: Array.from(frame._subtreeLifecycleEvents),
}, true);
});
this._frame = frame;
this.addObjectListener(Frame.Events.AddLifecycle, lifecycleEvent => {
this._dispatchEvent('loadstate', { add: lifecycleEvent });
@ -62,7 +62,7 @@ export class FrameDispatcher extends Dispatcher<Frame, channels.FrameChannel, Pa
return;
const params = { url: event.url, name: event.name, error: event.error ? event.error.message : undefined };
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);
});
}
@ -72,7 +72,7 @@ export class FrameDispatcher extends Dispatcher<Frame, channels.FrameChannel, Pa
}
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> {
@ -80,11 +80,11 @@ export class FrameDispatcher extends Dispatcher<Frame, channels.FrameChannel, Pa
}
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> {
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> {
@ -100,12 +100,12 @@ export class FrameDispatcher extends Dispatcher<Frame, channels.FrameChannel, Pa
}
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> {
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> {
@ -121,11 +121,11 @@ export class FrameDispatcher extends Dispatcher<Frame, channels.FrameChannel, Pa
}
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> {
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> {
@ -245,7 +245,7 @@ export class FrameDispatcher extends Dispatcher<Frame, channels.FrameChannel, Pa
}
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> {

View File

@ -41,19 +41,19 @@ export class JSHandleDispatcher extends Dispatcher<js.JSHandle, channels.JSHandl
async evaluateExpressionHandle(params: channels.JSHandleEvaluateExpressionHandleParams): Promise<channels.JSHandleEvaluateExpressionHandleResult> {
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> {
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> {
const map = await this._object.getProperties();
const properties = [];
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 };
}

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;
static from(scope: RootDispatcher | BrowserContextDispatcher, request: APIRequestContext): APIRequestContextDispatcher {
@ -182,7 +182,7 @@ export class APIRequestContextDispatcher extends Dispatcher<APIRequestContext, c
super(parentScope, request, 'APIRequestContext', {
tracing,
}, true);
});
this.adopt(tracing);
}

View File

@ -38,7 +38,7 @@ import type { Download } from '../download';
import { createGuid } from '../../utils';
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_Page = true;
private _page: Page;
@ -66,7 +66,7 @@ export class PageDispatcher extends Dispatcher<Page, channels.PageChannel, Brows
viewportSize: page.viewportSize() || undefined,
isClosed: page.isClosed(),
opener: PageDispatcher.fromNullable(parentScope, page.opener())
}, true);
});
this.adopt(mainFrame);
@ -75,22 +75,22 @@ export class PageDispatcher extends Dispatcher<Page, channels.PageChannel, Brows
this._dispatchEvent('close');
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.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) => {
// 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.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()
}));
this.addObjectListener(Page.Events.FrameAttached, frame => this._onFrameAttached(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.WebSocket, webSocket => this._dispatchEvent('webSocket', { webSocket: new WebSocketDispatcher(this._scope, webSocket) }));
this.addObjectListener(Page.Events.Worker, worker => this._dispatchEvent('worker', { worker: new WorkerDispatcher(this._scope, worker) }));
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, worker) }));
this.addObjectListener(Page.Events.Video, (artifact: Artifact) => this._dispatchEvent('video', { artifact: existingDispatcher<ArtifactDispatcher>(artifact) }));
if (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> {
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 });
return binding.promise();
});
@ -159,7 +159,7 @@ export class PageDispatcher extends Dispatcher<Page, channels.PageChannel, Brows
return;
}
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) {
this._dispatchEvent('frameAttached', { frame: FrameDispatcher.from(this._scope, frame) });
this._dispatchEvent('frameAttached', { frame: FrameDispatcher.from(this, 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;
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) {
super(scope, worker, 'Worker', {
url: worker.url()
}, true);
});
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> {
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),
preLaunchedBrowser: browserDispatcher,
socksSupport: socksProxy ? new SocksSupportDispatcher(scope, socksProxy) : undefined,
}, false);
});
this._type_Playwright = true;
this._browserDispatcher = browserDispatcher;
}
async newRequest(params: channels.PlaywrightNewRequestParams, metadata?: channels.Metadata): Promise<channels.PlaywrightNewRequestResult> {
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> {

View File

@ -15,16 +15,16 @@
*/
import type * as channels from '../../protocol/channels';
import type { DispatcherScope } from './dispatcher';
import { Dispatcher } from './dispatcher';
import type * as stream from 'stream';
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;
private _ended: boolean = false;
constructor(scope: DispatcherScope, stream: stream.Readable) {
constructor(scope: ArtifactDispatcher, stream: stream.Readable) {
super(scope, { guid: 'stream@' + createGuid(), stream }, 'Stream', {});
// In Node v12.9.0+ we can use readableEnded.
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) {
super(scope, tracing, 'Tracing', {}, true);
super(scope, tracing, 'Tracing', {});
}
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> {
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> {