mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00
chore: merge Connection.{close,didDisconnect} (#9524)
This simplifes cleanup logic. Also markAsRemote() in gridClient.
This commit is contained in:
parent
4680ef46de
commit
7a68f2f661
@ -20,7 +20,7 @@ import { Page } from './page';
|
|||||||
import { ChannelOwner } from './channelOwner';
|
import { ChannelOwner } from './channelOwner';
|
||||||
import { Events } from './events';
|
import { Events } from './events';
|
||||||
import { BrowserContextOptions } from './types';
|
import { BrowserContextOptions } from './types';
|
||||||
import { isSafeCloseError } from '../utils/errors';
|
import { isSafeCloseError, kBrowserClosedError } from '../utils/errors';
|
||||||
import * as api from '../../types/types';
|
import * as api from '../../types/types';
|
||||||
import { CDPSession } from './cdpSession';
|
import { CDPSession } from './cdpSession';
|
||||||
import type { BrowserType } from './browserType';
|
import type { BrowserType } from './browserType';
|
||||||
@ -110,7 +110,7 @@ export class Browser extends ChannelOwner<channels.BrowserChannel, channels.Brow
|
|||||||
try {
|
try {
|
||||||
await this._wrapApiCall(async (channel: channels.BrowserChannel) => {
|
await this._wrapApiCall(async (channel: channels.BrowserChannel) => {
|
||||||
if (this._shouldCloseConnectionOnClose)
|
if (this._shouldCloseConnectionOnClose)
|
||||||
this._connection.close();
|
this._connection.close(kBrowserClosedError);
|
||||||
else
|
else
|
||||||
await channel.close();
|
await channel.close();
|
||||||
await this._closedPromise;
|
await this._closedPromise;
|
||||||
|
@ -133,8 +133,9 @@ export class BrowserType extends ChannelOwner<channels.BrowserTypeChannel, chann
|
|||||||
let browser: Browser;
|
let browser: Browser;
|
||||||
const { pipe } = await channel.connect({ wsEndpoint, headers: params.headers, slowMo: params.slowMo, timeout: params.timeout });
|
const { pipe } = await channel.connect({ wsEndpoint, headers: params.headers, slowMo: params.slowMo, timeout: params.timeout });
|
||||||
const closePipe = () => pipe.close().catch(() => {});
|
const closePipe = () => pipe.close().catch(() => {});
|
||||||
const connection = new Connection(closePipe);
|
const connection = new Connection();
|
||||||
connection.markAsRemote();
|
connection.markAsRemote();
|
||||||
|
connection.on('close', closePipe);
|
||||||
|
|
||||||
const onPipeClosed = () => {
|
const onPipeClosed = () => {
|
||||||
// Emulate all pages, contexts and the browser closing upon disconnect.
|
// Emulate all pages, contexts and the browser closing upon disconnect.
|
||||||
@ -144,15 +145,14 @@ export class BrowserType extends ChannelOwner<channels.BrowserTypeChannel, chann
|
|||||||
context._onClose();
|
context._onClose();
|
||||||
}
|
}
|
||||||
browser?._didClose();
|
browser?._didClose();
|
||||||
connection.didDisconnect(kBrowserClosedError);
|
connection.close(kBrowserClosedError);
|
||||||
};
|
};
|
||||||
pipe.on('closed', onPipeClosed);
|
pipe.on('closed', onPipeClosed);
|
||||||
connection.onmessage = message => pipe.send({ message }).catch(onPipeClosed);
|
connection.onmessage = message => pipe.send({ message }).catch(onPipeClosed);
|
||||||
|
|
||||||
pipe.on('message', ({ message }) => {
|
pipe.on('message', ({ message }) => {
|
||||||
try {
|
try {
|
||||||
if (!connection!.isDisconnected())
|
connection!.dispatch(message);
|
||||||
connection!.dispatch(message);
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(`Playwright: Connection dispatch error`);
|
console.error(`Playwright: Connection dispatch error`);
|
||||||
console.error(e);
|
console.error(e);
|
||||||
@ -176,10 +176,7 @@ export class BrowserType extends ChannelOwner<channels.BrowserTypeChannel, chann
|
|||||||
browser._logger = logger;
|
browser._logger = logger;
|
||||||
browser._shouldCloseConnectionOnClose = true;
|
browser._shouldCloseConnectionOnClose = true;
|
||||||
browser._setBrowserType((playwright as any)[browser._name]);
|
browser._setBrowserType((playwright as any)[browser._name]);
|
||||||
browser.on(Events.Browser.Disconnected, () => {
|
browser.on(Events.Browser.Disconnected, closePipe);
|
||||||
playwright._cleanup();
|
|
||||||
closePipe();
|
|
||||||
});
|
|
||||||
fulfill(browser);
|
fulfill(browser);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
reject(e);
|
reject(e);
|
||||||
|
@ -60,14 +60,12 @@ export class Connection extends EventEmitter {
|
|||||||
private _lastId = 0;
|
private _lastId = 0;
|
||||||
private _callbacks = new Map<number, { resolve: (a: any) => void, reject: (a: Error) => void, stackTrace: ParsedStackTrace }>();
|
private _callbacks = new Map<number, { resolve: (a: any) => void, reject: (a: Error) => void, stackTrace: ParsedStackTrace }>();
|
||||||
private _rootObject: Root;
|
private _rootObject: Root;
|
||||||
private _disconnectedErrorMessage: string | undefined;
|
private _closedErrorMessage: string | undefined;
|
||||||
private _onClose?: () => void;
|
|
||||||
private _isRemote = false;
|
private _isRemote = false;
|
||||||
|
|
||||||
constructor(onClose?: () => void) {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
this._rootObject = new Root(this);
|
this._rootObject = new Root(this);
|
||||||
this._onClose = onClose;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
markAsRemote() {
|
markAsRemote() {
|
||||||
@ -91,6 +89,9 @@ export class Connection extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async sendMessageToServer(object: ChannelOwner, method: string, params: any, maybeStackTrace: ParsedStackTrace | null): Promise<any> {
|
async sendMessageToServer(object: ChannelOwner, method: string, params: any, maybeStackTrace: ParsedStackTrace | null): Promise<any> {
|
||||||
|
if (this._closedErrorMessage)
|
||||||
|
throw new Error(this._closedErrorMessage);
|
||||||
|
|
||||||
const guid = object._guid;
|
const guid = object._guid;
|
||||||
const stackTrace: ParsedStackTrace = maybeStackTrace || { frameTexts: [], frames: [], apiName: '', allFrames: [] };
|
const stackTrace: ParsedStackTrace = maybeStackTrace || { frameTexts: [], frames: [], apiName: '', allFrames: [] };
|
||||||
const { frames, apiName } = stackTrace;
|
const { frames, apiName } = stackTrace;
|
||||||
@ -102,8 +103,6 @@ export class Connection extends EventEmitter {
|
|||||||
const metadata: channels.Metadata = { stack: frames, apiName };
|
const metadata: channels.Metadata = { stack: frames, apiName };
|
||||||
this.onmessage({ ...converted, metadata });
|
this.onmessage({ ...converted, metadata });
|
||||||
|
|
||||||
if (this._disconnectedErrorMessage)
|
|
||||||
throw new Error(this._disconnectedErrorMessage);
|
|
||||||
return await new Promise((resolve, reject) => this._callbacks.set(id, { resolve, reject, stackTrace }));
|
return await new Promise((resolve, reject) => this._callbacks.set(id, { resolve, reject, stackTrace }));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -112,6 +111,9 @@ export class Connection extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dispatch(message: object) {
|
dispatch(message: object) {
|
||||||
|
if (this._closedErrorMessage)
|
||||||
|
return;
|
||||||
|
|
||||||
const { id, guid, method, params, result, error } = message as any;
|
const { id, guid, method, params, result, error } = message as any;
|
||||||
if (id) {
|
if (id) {
|
||||||
debugLogger.log('channel:response', message);
|
debugLogger.log('channel:response', message);
|
||||||
@ -144,21 +146,12 @@ export class Connection extends EventEmitter {
|
|||||||
object._channel.emit(method, object._type === 'JsonPipe' ? params : this._replaceGuidsWithChannels(params));
|
object._channel.emit(method, object._type === 'JsonPipe' ? params : this._replaceGuidsWithChannels(params));
|
||||||
}
|
}
|
||||||
|
|
||||||
close() {
|
close(errorMessage: string = 'Connection closed') {
|
||||||
if (this._onClose)
|
this._closedErrorMessage = errorMessage;
|
||||||
this._onClose();
|
|
||||||
}
|
|
||||||
|
|
||||||
didDisconnect(errorMessage: string) {
|
|
||||||
this._disconnectedErrorMessage = errorMessage;
|
|
||||||
for (const callback of this._callbacks.values())
|
for (const callback of this._callbacks.values())
|
||||||
callback.reject(new Error(errorMessage));
|
callback.reject(new Error(errorMessage));
|
||||||
this._callbacks.clear();
|
this._callbacks.clear();
|
||||||
this.emit('disconnect');
|
this.emit('close');
|
||||||
}
|
|
||||||
|
|
||||||
isDisconnected() {
|
|
||||||
return !!this._disconnectedErrorMessage;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private _replaceGuidsWithChannels(payload: any): any {
|
private _replaceGuidsWithChannels(payload: any): any {
|
||||||
|
@ -49,7 +49,6 @@ export class Playwright extends ChannelOwner<channels.PlaywrightChannel, channel
|
|||||||
readonly selectors: Selectors;
|
readonly selectors: Selectors;
|
||||||
readonly request: Fetch;
|
readonly request: Fetch;
|
||||||
readonly errors: { TimeoutError: typeof TimeoutError };
|
readonly errors: { TimeoutError: typeof TimeoutError };
|
||||||
private _selectorsOwner: SelectorsOwner;
|
|
||||||
private _sockets = new Map<string, net.Socket>();
|
private _sockets = new Map<string, net.Socket>();
|
||||||
private _redirectPortForTest: number | undefined;
|
private _redirectPortForTest: number | undefined;
|
||||||
|
|
||||||
@ -67,8 +66,13 @@ export class Playwright extends ChannelOwner<channels.PlaywrightChannel, channel
|
|||||||
this.selectors = sharedSelectors;
|
this.selectors = sharedSelectors;
|
||||||
this.errors = { TimeoutError };
|
this.errors = { TimeoutError };
|
||||||
|
|
||||||
this._selectorsOwner = SelectorsOwner.from(initializer.selectors);
|
const selectorsOwner = SelectorsOwner.from(initializer.selectors);
|
||||||
this.selectors._addChannel(this._selectorsOwner);
|
this.selectors._addChannel(selectorsOwner);
|
||||||
|
this._connection.on('close', () => {
|
||||||
|
this.selectors._removeChannel(selectorsOwner);
|
||||||
|
for (const uid of this._sockets.keys())
|
||||||
|
this._onSocksClosed(uid);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
_enablePortForwarding(redirectPortForTest?: number) {
|
_enablePortForwarding(redirectPortForTest?: number) {
|
||||||
@ -116,8 +120,4 @@ export class Playwright extends ChannelOwner<channels.PlaywrightChannel, channel
|
|||||||
this._sockets.get(uid)?.destroy();
|
this._sockets.get(uid)?.destroy();
|
||||||
this._sockets.delete(uid);
|
this._sockets.delete(uid);
|
||||||
}
|
}
|
||||||
|
|
||||||
_cleanup() {
|
|
||||||
this.selectors._removeChannel(this._selectorsOwner);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -34,9 +34,10 @@ export class GridClient {
|
|||||||
if (errorText)
|
if (errorText)
|
||||||
throw errorText;
|
throw errorText;
|
||||||
const connection = new Connection();
|
const connection = new Connection();
|
||||||
|
connection.markAsRemote();
|
||||||
connection.onmessage = (message: Object) => ws.send(JSON.stringify(message));
|
connection.onmessage = (message: Object) => ws.send(JSON.stringify(message));
|
||||||
ws.on('message', message => connection.dispatch(JSON.parse(message.toString())));
|
ws.on('message', message => connection.dispatch(JSON.parse(message.toString())));
|
||||||
ws.on('close', (code, reason) => connection.didDisconnect(reason));
|
ws.on('close', (code, reason) => connection.close(reason));
|
||||||
const playwright = await connection.initializePlaywright();
|
const playwright = await connection.initializePlaywright();
|
||||||
playwright._enablePortForwarding();
|
playwright._enablePortForwarding();
|
||||||
return new GridClient(ws, playwright);
|
return new GridClient(ws, playwright);
|
||||||
|
@ -49,9 +49,7 @@ export class PlaywrightClient {
|
|||||||
playwright = await connection.initializePlaywright();
|
playwright = await connection.initializePlaywright();
|
||||||
resolve(new PlaywrightClient(playwright, ws));
|
resolve(new PlaywrightClient(playwright, ws));
|
||||||
});
|
});
|
||||||
ws.on('close', () => {
|
ws.on('close', (code, reason) => connection.close(reason));
|
||||||
playwright?._cleanup();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
let timer: NodeJS.Timeout;
|
let timer: NodeJS.Timeout;
|
||||||
try {
|
try {
|
||||||
|
@ -475,7 +475,7 @@ test('should properly disconnect when connection closes from the client side', a
|
|||||||
await disconnectedPromise;
|
await disconnectedPromise;
|
||||||
expect(browser.isConnected()).toBe(false);
|
expect(browser.isConnected()).toBe(false);
|
||||||
|
|
||||||
expect((await navigationPromise).message).toContain('has been closed');
|
expect((await navigationPromise).message).toContain('Connection closed');
|
||||||
expect((await waitForNavigationPromise).message).toContain('Navigation failed because page was closed');
|
expect((await waitForNavigationPromise).message).toContain('Navigation failed because page was closed');
|
||||||
expect((await page.goto(server.EMPTY_PAGE).catch(e => e)).message).toContain('has been closed');
|
expect((await page.goto(server.EMPTY_PAGE).catch(e => e)).message).toContain('has been closed');
|
||||||
expect((await page.waitForNavigation().catch(e => e)).message).toContain('Navigation failed because page was closed');
|
expect((await page.waitForNavigation().catch(e => e)).message).toContain('Navigation failed because page was closed');
|
||||||
|
Loading…
x
Reference in New Issue
Block a user