chore: unregister handlers when dispatcher goes (#15425)
This commit is contained in:
Родитель
b9e41df0a5
Коммит
f71b620de7
|
@ -56,8 +56,8 @@ export class AndroidDeviceDispatcher extends Dispatcher<AndroidDevice, channels.
|
|||
}, true);
|
||||
for (const webView of device.webViews())
|
||||
this._dispatchEvent('webViewAdded', { webView });
|
||||
device.on(AndroidDevice.Events.WebViewAdded, webView => this._dispatchEvent('webViewAdded', { webView }));
|
||||
device.on(AndroidDevice.Events.WebViewRemoved, socketName => this._dispatchEvent('webViewRemoved', { socketName }));
|
||||
this.addObjectListener(AndroidDevice.Events.WebViewAdded, webView => this._dispatchEvent('webViewAdded', { webView }));
|
||||
this.addObjectListener(AndroidDevice.Events.WebViewRemoved, socketName => this._dispatchEvent('webViewRemoved', { socketName }));
|
||||
}
|
||||
|
||||
async wait(params: channels.AndroidDeviceWaitParams) {
|
||||
|
@ -179,8 +179,8 @@ export class AndroidSocketDispatcher extends Dispatcher<SocketBackend, channels.
|
|||
|
||||
constructor(scope: DispatcherScope, socket: SocketBackend) {
|
||||
super(scope, socket, 'AndroidSocket', {}, true);
|
||||
socket.on('data', (data: Buffer) => this._dispatchEvent('data', { data }));
|
||||
socket.on('close', () => {
|
||||
this.addObjectListener('data', (data: Buffer) => this._dispatchEvent('data', { data }));
|
||||
this.addObjectListener('close', () => {
|
||||
this._dispatchEvent('close');
|
||||
this._dispose();
|
||||
});
|
||||
|
|
|
@ -55,7 +55,7 @@ export class BrowserContextDispatcher extends Dispatcher<BrowserContext, channel
|
|||
const artifactDispatcher = new ArtifactDispatcher(scope, artifact);
|
||||
this._dispatchEvent('video', { artifact: artifactDispatcher });
|
||||
};
|
||||
context.on(BrowserContext.Events.VideoStarted, onVideo);
|
||||
this.addObjectListener(BrowserContext.Events.VideoStarted, onVideo);
|
||||
for (const video of context._browser._idToVideo.values()) {
|
||||
if (video.context === context)
|
||||
onVideo(video.artifact);
|
||||
|
@ -63,8 +63,8 @@ export class BrowserContextDispatcher extends Dispatcher<BrowserContext, channel
|
|||
|
||||
for (const page of context.pages())
|
||||
this._dispatchEvent('page', { page: new PageDispatcher(this._scope, page) });
|
||||
context.on(BrowserContext.Events.Page, page => this._dispatchEvent('page', { page: new PageDispatcher(this._scope, page) }));
|
||||
context.on(BrowserContext.Events.Close, () => {
|
||||
this.addObjectListener(BrowserContext.Events.Page, page => this._dispatchEvent('page', { page: new PageDispatcher(this._scope, page) }));
|
||||
this.addObjectListener(BrowserContext.Events.Close, () => {
|
||||
this._dispatchEvent('close');
|
||||
this._dispose();
|
||||
});
|
||||
|
@ -72,28 +72,28 @@ 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: new PageDispatcher(this._scope, page) });
|
||||
context.on(CRBrowserContext.CREvents.BackgroundPage, page => this._dispatchEvent('backgroundPage', { page: new PageDispatcher(this._scope, page) }));
|
||||
this.addObjectListener(CRBrowserContext.CREvents.BackgroundPage, page => this._dispatchEvent('backgroundPage', { page: new PageDispatcher(this._scope, page) }));
|
||||
for (const serviceWorker of (context as CRBrowserContext).serviceWorkers())
|
||||
this._dispatchEvent('serviceWorker', { worker: new WorkerDispatcher(this._scope, serviceWorker) });
|
||||
context.on(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._scope, serviceWorker) }));
|
||||
}
|
||||
context.on(BrowserContext.Events.Request, (request: Request) => {
|
||||
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())
|
||||
});
|
||||
});
|
||||
context.on(BrowserContext.Events.Response, (response: Response) => this._dispatchEvent('response', {
|
||||
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())
|
||||
}));
|
||||
context.on(BrowserContext.Events.RequestFailed, (request: Request) => this._dispatchEvent('requestFailed', {
|
||||
this.addObjectListener(BrowserContext.Events.RequestFailed, (request: Request) => this._dispatchEvent('requestFailed', {
|
||||
request: RequestDispatcher.from(this._scope, request),
|
||||
failureText: request._failureText || undefined,
|
||||
responseEndTiming: request._responseEndTiming,
|
||||
page: PageDispatcher.fromNullable(this._scope, request.frame()?._page.initializedOrUndefined())
|
||||
}));
|
||||
context.on(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(scope, request),
|
||||
response: ResponseDispatcher.fromNullable(scope, response),
|
||||
responseEndTiming: request._responseEndTiming,
|
||||
|
|
|
@ -31,7 +31,7 @@ export class BrowserDispatcher extends Dispatcher<Browser, channels.BrowserChann
|
|||
_type_Browser = true;
|
||||
constructor(scope: DispatcherScope, browser: Browser) {
|
||||
super(scope, browser, 'Browser', { version: browser.version(), name: browser.options.name }, true);
|
||||
browser.on(Browser.Events.Disconnected, () => this._didClose());
|
||||
this.addObjectListener(Browser.Events.Disconnected, () => this._didClose());
|
||||
}
|
||||
|
||||
_didClose() {
|
||||
|
|
|
@ -28,7 +28,7 @@ export class CDPSessionDispatcher extends Dispatcher<CRSession, channels.CDPSess
|
|||
crSession._eventListener = (method, params) => {
|
||||
this._dispatchEvent('event', { method, params });
|
||||
};
|
||||
crSession.on(CRSessionEvents.Disconnected, () => this._dispose());
|
||||
this.addObjectListener(CRSessionEvents.Disconnected, () => this._dispose());
|
||||
}
|
||||
|
||||
async send(params: channels.CDPSessionSendParams): Promise<channels.CDPSessionSendResult> {
|
||||
|
|
|
@ -24,6 +24,8 @@ import type { CallMetadata } from '../instrumentation';
|
|||
import { SdkObject } from '../instrumentation';
|
||||
import { rewriteErrorMessage } from '../../utils/stackTrace';
|
||||
import type { PlaywrightDispatcher } from './playwrightDispatcher';
|
||||
import { eventsHelper } from '../..//utils/eventsHelper';
|
||||
import type { RegisteredListener } from '../..//utils/eventsHelper';
|
||||
|
||||
export const dispatcherSymbol = Symbol('dispatcher');
|
||||
const metadataValidator = createMetadataValidator();
|
||||
|
@ -50,6 +52,7 @@ export class Dispatcher<Type extends { guid: string }, ChannelType> extends Even
|
|||
// Only "isScope" channel owners have registered dispatchers inside.
|
||||
private _dispatchers = new Map<string, Dispatcher<any, any>>();
|
||||
protected _disposed = false;
|
||||
protected _eventListeners: RegisteredListener[] = [];
|
||||
|
||||
readonly _guid: string;
|
||||
readonly _type: string;
|
||||
|
@ -81,6 +84,10 @@ export class Dispatcher<Type extends { guid: string }, ChannelType> extends Even
|
|||
this._connection.sendCreate(this._parent, type, guid, initializer, this._parent._object);
|
||||
}
|
||||
|
||||
addObjectListener(eventName: (string | symbol), handler: (...args: any[]) => void) {
|
||||
this._eventListeners.push(eventsHelper.addEventListener(this._object as unknown as EventEmitter, eventName, handler));
|
||||
}
|
||||
|
||||
_dispatchEvent<T extends keyof channels.EventsTraits<ChannelType>>(method: T, params?: channels.EventsTraits<ChannelType>[T]) {
|
||||
if (this._disposed) {
|
||||
if (isUnderTest())
|
||||
|
@ -95,6 +102,7 @@ export class Dispatcher<Type extends { guid: string }, ChannelType> extends Even
|
|||
protected _dispose() {
|
||||
assert(!this._disposed);
|
||||
this._disposed = true;
|
||||
eventsHelper.removeEventListeners(this._eventListeners);
|
||||
|
||||
// Clean up from parent and connection.
|
||||
if (this._parent)
|
||||
|
|
|
@ -44,7 +44,7 @@ export class ElectronApplicationDispatcher extends Dispatcher<ElectronApplicatio
|
|||
super(scope, electronApplication, 'ElectronApplication', {
|
||||
context: new BrowserContextDispatcher(scope, electronApplication.context())
|
||||
}, true);
|
||||
electronApplication.on(ElectronApplication.Events.Close, () => {
|
||||
this.addObjectListener(ElectronApplication.Events.Close, () => {
|
||||
this._dispatchEvent('close');
|
||||
this._dispose();
|
||||
});
|
||||
|
|
|
@ -51,13 +51,13 @@ export class FrameDispatcher extends Dispatcher<Frame, channels.FrameChannel> im
|
|||
loadStates: Array.from(frame._subtreeLifecycleEvents),
|
||||
});
|
||||
this._frame = frame;
|
||||
frame.on(Frame.Events.AddLifecycle, lifecycleEvent => {
|
||||
this.addObjectListener(Frame.Events.AddLifecycle, lifecycleEvent => {
|
||||
this._dispatchEvent('loadstate', { add: lifecycleEvent });
|
||||
});
|
||||
frame.on(Frame.Events.RemoveLifecycle, lifecycleEvent => {
|
||||
this.addObjectListener(Frame.Events.RemoveLifecycle, lifecycleEvent => {
|
||||
this._dispatchEvent('loadstate', { remove: lifecycleEvent });
|
||||
});
|
||||
frame.on(Frame.Events.InternalNavigation, (event: NavigationEvent) => {
|
||||
this.addObjectListener(Frame.Events.InternalNavigation, (event: NavigationEvent) => {
|
||||
if (!event.isPublic)
|
||||
return;
|
||||
const params = { url: event.url, name: event.name, error: event.error ? event.error.message : undefined };
|
||||
|
|
|
@ -153,10 +153,10 @@ export class WebSocketDispatcher extends Dispatcher<WebSocket, channels.WebSocke
|
|||
super(scope, webSocket, 'WebSocket', {
|
||||
url: webSocket.url(),
|
||||
});
|
||||
webSocket.on(WebSocket.Events.FrameSent, (event: { opcode: number, data: string }) => this._dispatchEvent('frameSent', event));
|
||||
webSocket.on(WebSocket.Events.FrameReceived, (event: { opcode: number, data: string }) => this._dispatchEvent('frameReceived', event));
|
||||
webSocket.on(WebSocket.Events.SocketError, (error: string) => this._dispatchEvent('socketError', { error }));
|
||||
webSocket.on(WebSocket.Events.Close, () => this._dispatchEvent('close', {}));
|
||||
this.addObjectListener(WebSocket.Events.FrameSent, (event: { opcode: number, data: string }) => this._dispatchEvent('frameSent', event));
|
||||
this.addObjectListener(WebSocket.Events.FrameReceived, (event: { opcode: number, data: string }) => this._dispatchEvent('frameReceived', event));
|
||||
this.addObjectListener(WebSocket.Events.SocketError, (error: string) => this._dispatchEvent('socketError', { error }));
|
||||
this.addObjectListener(WebSocket.Events.Close, () => this._dispatchEvent('close', {}));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -59,26 +59,26 @@ export class PageDispatcher extends Dispatcher<Page, channels.PageChannel> imple
|
|||
opener: PageDispatcher.fromNullable(scope, page.opener())
|
||||
}, true);
|
||||
this._page = page;
|
||||
page.on(Page.Events.Close, () => {
|
||||
this.addObjectListener(Page.Events.Close, () => {
|
||||
this._dispatchEvent('close');
|
||||
this._dispose();
|
||||
});
|
||||
page.on(Page.Events.Console, message => this._dispatchEvent('console', { message: new ConsoleMessageDispatcher(this._scope, message) }));
|
||||
page.on(Page.Events.Crash, () => this._dispatchEvent('crash'));
|
||||
page.on(Page.Events.Dialog, dialog => this._dispatchEvent('dialog', { dialog: new DialogDispatcher(this._scope, dialog) }));
|
||||
page.on(Page.Events.Download, (download: Download) => {
|
||||
this.addObjectListener(Page.Events.Console, message => this._dispatchEvent('console', { message: new ConsoleMessageDispatcher(this._scope, 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.Download, (download: Download) => {
|
||||
this._dispatchEvent('download', { url: download.url, suggestedFilename: download.suggestedFilename(), artifact: new ArtifactDispatcher(scope, download.artifact) });
|
||||
});
|
||||
this._page.on(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()),
|
||||
isMultiple: fileChooser.isMultiple()
|
||||
}));
|
||||
page.on(Page.Events.FrameAttached, frame => this._onFrameAttached(frame));
|
||||
page.on(Page.Events.FrameDetached, frame => this._onFrameDetached(frame));
|
||||
page.on(Page.Events.PageError, error => this._dispatchEvent('pageError', { error: serializeError(error) }));
|
||||
page.on(Page.Events.WebSocket, webSocket => this._dispatchEvent('webSocket', { webSocket: new WebSocketDispatcher(this._scope, webSocket) }));
|
||||
page.on(Page.Events.Worker, worker => this._dispatchEvent('worker', { worker: new WorkerDispatcher(this._scope, worker) }));
|
||||
page.on(Page.Events.Video, (artifact: Artifact) => this._dispatchEvent('video', { artifact: existingDispatcher<ArtifactDispatcher>(artifact) }));
|
||||
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.Video, (artifact: Artifact) => this._dispatchEvent('video', { artifact: existingDispatcher<ArtifactDispatcher>(artifact) }));
|
||||
if (page._video)
|
||||
this._dispatchEvent('video', { artifact: existingDispatcher<ArtifactDispatcher>(page._video) });
|
||||
// Ensure client knows about all frames.
|
||||
|
@ -299,7 +299,7 @@ export class WorkerDispatcher extends Dispatcher<Worker, channels.WorkerChannel>
|
|||
super(scope, worker, 'Worker', {
|
||||
url: worker.url()
|
||||
});
|
||||
worker.on(Worker.Events.Close, () => this._dispatchEvent('close'));
|
||||
this.addObjectListener(Worker.Events.Close, () => this._dispatchEvent('close'));
|
||||
}
|
||||
|
||||
async evaluateExpression(params: channels.WorkerEvaluateExpressionParams, metadata: CallMetadata): Promise<channels.WorkerEvaluateExpressionResult> {
|
||||
|
|
|
@ -30,7 +30,7 @@ export class TracingDispatcher extends Dispatcher<Tracing, channels.TracingChann
|
|||
|
||||
constructor(scope: DispatcherScope, tracing: Tracing) {
|
||||
super(scope, tracing, 'Tracing', {}, true);
|
||||
tracing.on(Tracing.Events.Dispose, () => this._dispose());
|
||||
this.addObjectListener(Tracing.Events.Dispose, () => this._dispose());
|
||||
}
|
||||
|
||||
async tracingStart(params: channels.TracingTracingStartParams): Promise<channels.TracingTracingStartResult> {
|
||||
|
|
Загрузка…
Ссылка в новой задаче