Skip to content

Commit 3318fa9

Browse files
authored
chore: replace more cleanupWhenAborted calls with try/catch (microsoft#36833)
1 parent 31d5434 commit 3318fa9

File tree

8 files changed

+218
-191
lines changed

8 files changed

+218
-191
lines changed

packages/playwright-core/src/server/chromium/crDragDrop.ts

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -91,26 +91,26 @@ export class DragManager {
9191
};
9292
}
9393

94-
await this._crPage._page.safeNonStallingEvaluateInAllFrames(`(${setupDragListeners.toString()})()`, 'utility');
95-
progress.cleanupWhenAborted(() => this._crPage._page.safeNonStallingEvaluateInAllFrames('window.__cleanupDrag && window.__cleanupDrag()', 'utility'));
96-
97-
client.on('Input.dragIntercepted', onDragIntercepted!);
9894
try {
95+
let expectingDrag = false;
96+
await progress.race(this._crPage._page.safeNonStallingEvaluateInAllFrames(`(${setupDragListeners.toString()})()`, 'utility'));
97+
client.on('Input.dragIntercepted', onDragIntercepted!);
9998
await client.send('Input.setInterceptDrags', { enabled: true });
100-
} catch {
101-
// If Input.setInterceptDrags is not supported, just do a regular move.
102-
// This can be removed once we stop supporting old Electron.
103-
client.off('Input.dragIntercepted', onDragIntercepted!);
104-
return moveCallback();
99+
try {
100+
await progress.race(moveCallback());
101+
expectingDrag = (await Promise.all(this._crPage._page.frames().map(async frame => {
102+
return frame.nonStallingEvaluateInExistingContext('window.__cleanupDrag?.()', 'utility').catch(() => false);
103+
}))).some(x => x);
104+
} finally {
105+
client.off('Input.dragIntercepted', onDragIntercepted!);
106+
await client.send('Input.setInterceptDrags', { enabled: false });
107+
}
108+
this._dragState = expectingDrag ? (await dragInterceptedPromise).data : null;
109+
} catch (error) {
110+
// Cleanup without blocking, it will be done before the next playwright action.
111+
this._crPage._page.safeNonStallingEvaluateInAllFrames('window.__cleanupDrag?.()', 'utility').catch(() => {});
112+
throw error;
105113
}
106-
await moveCallback();
107-
108-
const expectingDrag = (await Promise.all(this._crPage._page.frames().map(async frame => {
109-
return frame.nonStallingEvaluateInExistingContext('window.__cleanupDrag && window.__cleanupDrag()', 'utility').catch(() => false);
110-
}))).some(x => x);
111-
this._dragState = expectingDrag ? (await dragInterceptedPromise).data : null;
112-
client.off('Input.dragIntercepted', onDragIntercepted!);
113-
await progress.race(client.send('Input.setInterceptDrags', { enabled: false }));
114114

115115
if (this._dragState) {
116116
await progress.race(this._crPage._mainFrameSession._client.send('Input.dispatchDragEvent', {

packages/playwright-core/src/server/electron/electron.ts

Lines changed: 67 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import os from 'os';
1919
import path from 'path';
2020
import * as readline from 'readline';
2121

22-
import { ManualPromise, removeFolders } from '../../utils';
22+
import { ManualPromise } from '../../utils';
2323
import { wrapInASCIIBox } from '../utils/ascii';
2424
import { RecentLogsCollector } from '../utils/debugLogger';
2525
import { eventsHelper } from '../utils/eventsHelper';
@@ -166,8 +166,6 @@ export class Electron extends SdkObject {
166166
}
167167

168168
const artifactsDir = await progress.race(fs.promises.mkdtemp(ARTIFACTS_FOLDER));
169-
progress.cleanupWhenAborted(() => removeFolders([artifactsDir]));
170-
171169
const browserLogsCollector = new RecentLogsCollector();
172170
const env = options.env ? envArrayToObject(options.env) : process.env;
173171

@@ -243,76 +241,76 @@ export class Electron extends SdkObject {
243241
const chromeMatchPromise = waitForLine(progress, launchedProcess, /^DevTools listening on (ws:\/\/.*)$/);
244242
const debuggerDisconnectPromise = waitForLine(progress, launchedProcess, /Waiting for the debugger to disconnect\.\.\./);
245243

246-
const nodeMatch = await nodeMatchPromise;
247-
const nodeTransport = await WebSocketTransport.connect(progress, nodeMatch[1]);
248-
progress.cleanupWhenAborted(() => nodeTransport.close());
249-
const nodeConnection = new CRConnection(this, nodeTransport, helper.debugProtocolLogger(), browserLogsCollector);
244+
try {
245+
const nodeMatch = await nodeMatchPromise;
246+
const nodeTransport = await WebSocketTransport.connect(progress, nodeMatch[1]);
247+
const nodeConnection = new CRConnection(this, nodeTransport, helper.debugProtocolLogger(), browserLogsCollector);
248+
// Immediately release exiting process under debug.
249+
debuggerDisconnectPromise.then(() => {
250+
nodeTransport.close();
251+
}).catch(() => {});
250252

251-
// Immediately release exiting process under debug.
252-
debuggerDisconnectPromise.then(() => {
253-
nodeTransport.close();
254-
}).catch(() => {});
255-
const chromeMatch = await Promise.race([
256-
chromeMatchPromise,
257-
waitForXserverError,
258-
]) as RegExpMatchArray;
259-
const chromeTransport = await WebSocketTransport.connect(progress, chromeMatch[1]);
260-
progress.cleanupWhenAborted(() => chromeTransport.close());
261-
const browserProcess: BrowserProcess = {
262-
onclose: undefined,
263-
process: launchedProcess,
264-
close: gracefullyClose,
265-
kill
266-
};
267-
const contextOptions: types.BrowserContextOptions = {
268-
...options,
269-
noDefaultViewport: true,
270-
};
271-
const browserOptions: BrowserOptions = {
272-
name: 'electron',
273-
isChromium: true,
274-
headful: true,
275-
persistent: contextOptions,
276-
browserProcess,
277-
protocolLogger: helper.debugProtocolLogger(),
278-
browserLogsCollector,
279-
artifactsDir,
280-
downloadsPath: artifactsDir,
281-
tracesDir: options.tracesDir || artifactsDir,
282-
originalLaunchOptions: {},
283-
};
284-
validateBrowserContextOptions(contextOptions, browserOptions);
285-
const browser = await progress.race(CRBrowser.connect(this.attribution.playwright, chromeTransport, browserOptions));
286-
app = new ElectronApplication(this, browser, nodeConnection, launchedProcess);
287-
await progress.race(app.initialize());
288-
return app;
253+
const chromeMatch = await Promise.race([
254+
chromeMatchPromise,
255+
waitForXserverError,
256+
]);
257+
const chromeTransport = await WebSocketTransport.connect(progress, chromeMatch[1]);
258+
const browserProcess: BrowserProcess = {
259+
onclose: undefined,
260+
process: launchedProcess,
261+
close: gracefullyClose,
262+
kill
263+
};
264+
const contextOptions: types.BrowserContextOptions = {
265+
...options,
266+
noDefaultViewport: true,
267+
};
268+
const browserOptions: BrowserOptions = {
269+
name: 'electron',
270+
isChromium: true,
271+
headful: true,
272+
persistent: contextOptions,
273+
browserProcess,
274+
protocolLogger: helper.debugProtocolLogger(),
275+
browserLogsCollector,
276+
artifactsDir,
277+
downloadsPath: artifactsDir,
278+
tracesDir: options.tracesDir || artifactsDir,
279+
originalLaunchOptions: {},
280+
};
281+
validateBrowserContextOptions(contextOptions, browserOptions);
282+
const browser = await progress.race(CRBrowser.connect(this.attribution.playwright, chromeTransport, browserOptions));
283+
app = new ElectronApplication(this, browser, nodeConnection, launchedProcess);
284+
await progress.race(app.initialize());
285+
return app;
286+
} catch (error) {
287+
await kill();
288+
throw error;
289+
}
289290
}
290291
}
291292

292-
function waitForLine(progress: Progress, process: childProcess.ChildProcess, regex: RegExp): Promise<RegExpMatchArray> {
293-
return progress.race(new Promise((resolve, reject) => {
294-
const rl = readline.createInterface({ input: process.stderr! });
295-
const failError = new Error('Process failed to launch!');
296-
const listeners = [
297-
eventsHelper.addEventListener(rl, 'line', onLine),
298-
eventsHelper.addEventListener(rl, 'close', reject.bind(null, failError)),
299-
eventsHelper.addEventListener(process, 'exit', reject.bind(null, failError)),
300-
// It is Ok to remove error handler because we did not create process and there is another listener.
301-
eventsHelper.addEventListener(process, 'error', reject.bind(null, failError))
302-
];
293+
async function waitForLine(progress: Progress, process: childProcess.ChildProcess, regex: RegExp) {
294+
const promise = new ManualPromise<RegExpMatchArray>();
295+
const rl = readline.createInterface({ input: process.stderr! });
296+
const failError = new Error('Process failed to launch!');
297+
const listeners = [
298+
eventsHelper.addEventListener(rl, 'line', onLine),
299+
eventsHelper.addEventListener(rl, 'close', () => promise.reject(failError)),
300+
eventsHelper.addEventListener(process, 'exit', () => promise.reject(failError)),
301+
// It is Ok to remove error handler because we did not create process and there is another listener.
302+
eventsHelper.addEventListener(process, 'error', () => promise.reject(failError)),
303+
];
303304

304-
progress.cleanupWhenAborted(cleanup);
305-
306-
function onLine(line: string) {
307-
const match = line.match(regex);
308-
if (!match)
309-
return;
310-
cleanup();
311-
resolve(match);
312-
}
305+
function onLine(line: string) {
306+
const match = line.match(regex);
307+
if (match)
308+
promise.resolve(match);
309+
}
313310

314-
function cleanup() {
315-
eventsHelper.removeEventListeners(listeners);
316-
}
317-
}));
311+
try {
312+
return await progress.race(promise);
313+
} finally {
314+
eventsHelper.removeEventListeners(listeners);
315+
}
318316
}

packages/playwright-core/src/server/fetch.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,7 @@ export abstract class APIRequestContext extends SdkObject {
298298
};
299299
this.emit(APIRequestContext.Events.Request, requestEvent);
300300

301+
let destroyRequest: (() => void) | undefined;
301302
const resultPromise = new Promise<SendRequestResult>((fulfill, reject) => {
302303
const requestConstructor: ((url: URL, options: http.RequestOptions, callback?: (res: http.IncomingMessage) => void) => http.ClientRequest)
303304
= (url.protocol === 'https:' ? https : http).request;
@@ -483,7 +484,7 @@ export abstract class APIRequestContext extends SdkObject {
483484
body.on('end', notifyBodyFinished);
484485
});
485486
request.on('error', reject);
486-
progress.cleanupWhenAborted(() => request.destroy());
487+
destroyRequest = () => request.destroy();
487488

488489
listeners.push(
489490
eventsHelper.addEventListener(this, APIRequestContext.Events.Dispose, () => {
@@ -539,7 +540,11 @@ export abstract class APIRequestContext extends SdkObject {
539540
request.write(postData);
540541
request.end();
541542
});
542-
return progress.race(resultPromise);
543+
544+
return progress.race(resultPromise).catch(error => {
545+
destroyRequest?.();
546+
throw error;
547+
});
543548
}
544549

545550
private _getHttpCredentials(url: URL) {

packages/playwright-core/src/server/helper.ts

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -57,22 +57,19 @@ class Helper {
5757

5858
static waitForEvent(progress: Progress, emitter: EventEmitter, event: string | symbol, predicate?: Function): { promise: Promise<any>, dispose: () => void } {
5959
const listeners: RegisteredListener[] = [];
60-
const promise = new Promise((resolve, reject) => {
60+
const dispose = () => eventsHelper.removeEventListeners(listeners);
61+
const promise = progress.race(new Promise((resolve, reject) => {
6162
listeners.push(eventsHelper.addEventListener(emitter, event, eventArg => {
6263
try {
6364
if (predicate && !predicate(eventArg))
6465
return;
65-
eventsHelper.removeEventListeners(listeners);
6666
resolve(eventArg);
6767
} catch (e) {
68-
eventsHelper.removeEventListeners(listeners);
6968
reject(e);
7069
}
7170
}));
72-
});
73-
const dispose = () => eventsHelper.removeEventListeners(listeners);
74-
progress.cleanupWhenAborted(dispose);
75-
return { promise: progress.race(promise), dispose };
71+
})).finally(() => dispose());
72+
return { promise, dispose };
7673
}
7774

7875
static secondsToRoundishMillis(value: number): number {

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

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -179,16 +179,18 @@ export function harClose(harBackends: Map<string, HarBackend>, params: channels.
179179
export async function harUnzip(progress: Progress, params: channels.LocalUtilsHarUnzipParams): Promise<void> {
180180
const dir = path.dirname(params.zipFile);
181181
const zipFile = new ZipFile(params.zipFile);
182-
progress.cleanupWhenAborted(() => zipFile.close());
183-
for (const entry of await progress.race(zipFile.entries())) {
184-
const buffer = await progress.race(zipFile.read(entry));
185-
if (entry === 'har.har')
186-
await progress.race(fs.promises.writeFile(params.harFile, buffer));
187-
else
188-
await progress.race(fs.promises.writeFile(path.join(dir, entry), buffer));
182+
try {
183+
for (const entry of await progress.race(zipFile.entries())) {
184+
const buffer = await progress.race(zipFile.read(entry));
185+
if (entry === 'har.har')
186+
await progress.race(fs.promises.writeFile(params.harFile, buffer));
187+
else
188+
await progress.race(fs.promises.writeFile(path.join(dir, entry), buffer));
189+
}
190+
await progress.race(fs.promises.unlink(params.zipFile));
191+
} finally {
192+
zipFile.close();
189193
}
190-
await progress.race(fs.promises.unlink(params.zipFile));
191-
zipFile.close();
192194
}
193195

194196
export async function tracingStarted(progress: Progress, stackSessions: Map<string, StackSession>, params: channels.LocalUtilsTracingStartedParams): Promise<channels.LocalUtilsTracingStartedResult> {

0 commit comments

Comments
 (0)