Skip to content

Commit 51f944d

Browse files
authored
chore: extract pipe->connection code (#34689)
1 parent 2718ce7 commit 51f944d

File tree

8 files changed

+43
-64
lines changed

8 files changed

+43
-64
lines changed

packages/playwright-core/src/client/android.ts

Lines changed: 5 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ import { EventEmitter } from 'events';
1818

1919
import { BrowserContext, prepareBrowserContextParams } from './browserContext';
2020
import { ChannelOwner } from './channelOwner';
21-
import { Connection } from './connection';
2221
import { TargetClosedError, isTargetClosedError } from './errors';
2322
import { Events } from './events';
2423
import { Waiter } from './waiter';
@@ -72,45 +71,28 @@ export class Android extends ChannelOwner<channels.AndroidChannel> implements ap
7271
const headers = { 'x-playwright-browser': 'android', ...options.headers };
7372
const localUtils = this._connection.localUtils();
7473
const connectParams: channels.LocalUtilsConnectParams = { wsEndpoint, headers, slowMo: options.slowMo, timeout: options.timeout };
75-
const { pipe } = await localUtils.connect(connectParams);
76-
const closePipe = () => pipe.close().catch(() => {});
77-
const connection = new Connection(localUtils, this._platform, this._instrumentation);
78-
connection.markAsRemote();
79-
connection.on('close', closePipe);
74+
const connection = await localUtils.connect(connectParams);
8075

8176
let device: AndroidDevice;
82-
let closeError: string | undefined;
83-
const onPipeClosed = () => {
77+
connection.on('close', () => {
8478
device?._didClose();
85-
connection.close(closeError);
86-
};
87-
pipe.on('closed', onPipeClosed);
88-
connection.onmessage = message => pipe.send({ message }).catch(onPipeClosed);
89-
90-
pipe.on('message', ({ message }) => {
91-
try {
92-
connection!.dispatch(message);
93-
} catch (e) {
94-
closeError = String(e);
95-
closePipe();
96-
}
9779
});
9880

9981
const result = await raceAgainstDeadline(async () => {
10082
const playwright = await connection!.initializePlaywright();
10183
if (!playwright._initializer.preConnectedAndroidDevice) {
102-
closePipe();
84+
connection.close();
10385
throw new Error('Malformed endpoint. Did you use Android.launchServer method?');
10486
}
10587
device = AndroidDevice.from(playwright._initializer.preConnectedAndroidDevice!);
10688
device._shouldCloseConnectionOnClose = true;
107-
device.on(Events.AndroidDevice.Close, closePipe);
89+
device.on(Events.AndroidDevice.Close, () => connection.close());
10890
return device;
10991
}, deadline);
11092
if (!result.timedOut) {
11193
return result.result;
11294
} else {
113-
closePipe();
95+
connection.close();
11496
throw new Error(`Timeout ${options.timeout}ms exceeded`);
11597
}
11698
});

packages/playwright-core/src/client/browser.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import { mkdirIfNeeded } from '../utils/fileUtils';
2424

2525
import type { BrowserType } from './browserType';
2626
import type { Page } from './page';
27-
import type { BrowserContextOptions, HeadersArray, LaunchOptions } from './types';
27+
import type { BrowserContextOptions, LaunchOptions } from './types';
2828
import type * as api from '../../types/types';
2929
import type * as channels from '@protocol/channels';
3030

@@ -37,9 +37,6 @@ export class Browser extends ChannelOwner<channels.BrowserChannel> implements ap
3737
_options: LaunchOptions = {};
3838
readonly _name: string;
3939
private _path: string | undefined;
40-
41-
// Used from @playwright/test fixtures.
42-
_connectHeaders?: HeadersArray;
4340
_closeReason: string | undefined;
4441

4542
static from(browser: channels.BrowserChannel): Browser {

packages/playwright-core/src/client/browserType.ts

Lines changed: 5 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ import { Browser } from './browser';
1818
import { BrowserContext, prepareBrowserContextParams } from './browserContext';
1919
import { ChannelOwner } from './channelOwner';
2020
import { envObjectToArray } from './clientHelper';
21-
import { Connection } from './connection';
2221
import { Events } from './events';
2322
import { assert } from '../utils/debug';
2423
import { headersObjectToArray } from '../utils/headers';
@@ -133,40 +132,16 @@ export class BrowserType extends ChannelOwner<channels.BrowserTypeChannel> imple
133132
};
134133
if ((params as any).__testHookRedirectPortForwarding)
135134
connectParams.socksProxyRedirectPortForTest = (params as any).__testHookRedirectPortForwarding;
136-
const { pipe, headers: connectHeaders } = await localUtils.connect(connectParams);
137-
const closePipe = () => pipe.close().catch(() => {});
138-
const connection = new Connection(localUtils, this._platform, this._instrumentation);
139-
connection.markAsRemote();
140-
connection.on('close', closePipe);
141-
135+
const connection = await localUtils.connect(connectParams);
142136
let browser: Browser;
143-
let closeError: string | undefined;
144-
const onPipeClosed = (reason?: string) => {
137+
connection.on('close', () => {
145138
// Emulate all pages, contexts and the browser closing upon disconnect.
146139
for (const context of browser?.contexts() || []) {
147140
for (const page of context.pages())
148141
page._onClose();
149142
context._onClose();
150143
}
151-
connection.close(reason || closeError);
152-
// Give a chance to any API call promises to reject upon page/context closure.
153-
// This happens naturally when we receive page.onClose and browser.onClose from the server
154-
// in separate tasks. However, upon pipe closure we used to dispatch them all synchronously
155-
// here and promises did not have a chance to reject.
156-
// The order of rejects vs closure is a part of the API contract and our test runner
157-
// relies on it to attribute rejections to the right test.
158144
setTimeout(() => browser?._didClose(), 0);
159-
};
160-
pipe.on('closed', params => onPipeClosed(params.reason));
161-
connection.onmessage = message => this._wrapApiCall(() => pipe.send({ message }).catch(() => onPipeClosed()), /* isInternal */ true);
162-
163-
pipe.on('message', ({ message }) => {
164-
try {
165-
connection!.dispatch(message);
166-
} catch (e) {
167-
closeError = String(e);
168-
closePipe();
169-
}
170145
});
171146

172147
const result = await raceAgainstDeadline(async () => {
@@ -176,21 +151,20 @@ export class BrowserType extends ChannelOwner<channels.BrowserTypeChannel> imple
176151

177152
const playwright = await connection!.initializePlaywright();
178153
if (!playwright._initializer.preLaunchedBrowser) {
179-
closePipe();
154+
connection.close();
180155
throw new Error('Malformed endpoint. Did you use BrowserType.launchServer method?');
181156
}
182157
playwright._setSelectors(this._playwright.selectors);
183158
browser = Browser.from(playwright._initializer.preLaunchedBrowser!);
184159
this._didLaunchBrowser(browser, {}, logger);
185160
browser._shouldCloseConnectionOnClose = true;
186-
browser._connectHeaders = connectHeaders;
187-
browser.on(Events.Browser.Disconnected, () => this._wrapApiCall(() => closePipe(), /* isInternal */ true));
161+
browser.on(Events.Browser.Disconnected, () => connection.close());
188162
return browser;
189163
}, deadline);
190164
if (!result.timedOut) {
191165
return result.result;
192166
} else {
193-
closePipe();
167+
connection.close();
194168
throw new Error(`Timeout ${params.timeout}ms exceeded`);
195169
}
196170
});

packages/playwright-core/src/client/connection.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ import { formatCallLog, rewriteErrorMessage } from '../utils/stackTrace';
4747
import { zones } from '../utils/zones';
4848

4949
import type { ClientInstrumentation } from './clientInstrumentation';
50+
import type { HeadersArray } from './types';
5051
import type { ValidatorContext } from '../protocol/validator';
5152
import type { Platform } from '../utils/platform';
5253
import type * as channels from '@protocol/channels';
@@ -81,13 +82,16 @@ export class Connection extends EventEmitter {
8182
private _tracingCount = 0;
8283
readonly _instrumentation: ClientInstrumentation;
8384
readonly platform: Platform;
85+
// Used from @playwright/test fixtures -> TODO remove?
86+
readonly headers: HeadersArray;
8487

85-
constructor(localUtils: LocalUtils | undefined, platform: Platform, instrumentation: ClientInstrumentation | undefined) {
88+
constructor(localUtils: LocalUtils | undefined, platform: Platform, instrumentation: ClientInstrumentation | undefined, headers: HeadersArray) {
8689
super();
8790
this._instrumentation = instrumentation || createInstrumentation();
8891
this._localUtils = localUtils;
8992
this.platform = platform;
9093
this._rootObject = new Root(this);
94+
this.headers = headers;
9195
}
9296

9397
markAsRemote() {

packages/playwright-core/src/client/localUtils.ts

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616

1717
import { ChannelOwner } from './channelOwner';
18+
import { Connection } from './connection';
1819
import * as localUtils from '../utils/localUtils';
1920

2021
import type { Size } from './types';
@@ -76,7 +77,28 @@ export class LocalUtils extends ChannelOwner<channels.LocalUtilsChannel> {
7677
return await localUtils.addStackToTracingNoReply(this._stackSessions, params);
7778
}
7879

79-
async connect(params: channels.LocalUtilsConnectParams): Promise<channels.LocalUtilsConnectResult> {
80-
return await this._channel.connect(params);
80+
async connect(params: channels.LocalUtilsConnectParams): Promise<Connection> {
81+
const { pipe, headers: connectHeaders } = await this._channel.connect(params);
82+
const closePipe = () => this._wrapApiCall(() => pipe.close().catch(() => {}), /* isInternal */ true);
83+
const connection = new Connection(this, this._platform, this._instrumentation, connectHeaders);
84+
connection.markAsRemote();
85+
connection.on('close', closePipe);
86+
87+
let closeError: string | undefined;
88+
const onPipeClosed = (reason?: string) => {
89+
connection.close(reason || closeError);
90+
};
91+
pipe.on('closed', params => onPipeClosed(params.reason));
92+
connection.onmessage = message => this._wrapApiCall(() => pipe.send({ message }).catch(() => onPipeClosed()), /* isInternal */ true);
93+
94+
pipe.on('message', ({ message }) => {
95+
try {
96+
connection!.dispatch(message);
97+
} catch (e) {
98+
closeError = String(e);
99+
closePipe();
100+
}
101+
});
102+
return connection;
81103
}
82104
}

packages/playwright-core/src/inProcessFactory.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import type { Platform } from './utils/platform';
2626
export function createInProcessPlaywright(platform: Platform): PlaywrightAPI {
2727
const playwright = createPlaywright({ sdkLanguage: (process.env.PW_LANG_NAME as Language | undefined) || 'javascript' });
2828

29-
const clientConnection = new Connection(undefined, platform, undefined);
29+
const clientConnection = new Connection(undefined, platform, undefined, []);
3030
clientConnection.useRawBuffers();
3131
const dispatcherConnection = new DispatcherConnection(true /* local */);
3232

packages/playwright-core/src/outofprocess.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ class PlaywrightClient {
4848
this._driverProcess.unref();
4949
this._driverProcess.stderr!.on('data', data => process.stderr.write(data));
5050

51-
const connection = new Connection(undefined, nodePlatform, undefined);
51+
const connection = new Connection(undefined, nodePlatform, undefined, []);
5252
const transport = new PipeTransport(this._driverProcess.stdin!, this._driverProcess.stdout!);
5353
connection.onmessage = message => transport.send(JSON.stringify(message));
5454
transport.onmessage = message => connection.dispatch(JSON.parse(message));

packages/playwright/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -471,7 +471,7 @@ function normalizeScreenshotMode(screenshot: ScreenshotOption): ScreenshotMode {
471471
}
472472

473473
function attachConnectedHeaderIfNeeded(testInfo: TestInfo, browser: Browser | null) {
474-
const connectHeaders: { name: string, value: string }[] | undefined = (browser as any)?._connectHeaders;
474+
const connectHeaders: { name: string, value: string }[] | undefined = (browser as any)?._connection.headers;
475475
if (!connectHeaders)
476476
return;
477477
for (const header of connectHeaders) {

0 commit comments

Comments
 (0)