@@ -23,7 +23,7 @@ import { debugMode } from './utils/debug';
23
23
import { assert } from '../utils/isomorphic/assert' ;
24
24
import { ManualPromise } from '../utils/isomorphic/manualPromise' ;
25
25
import { DEFAULT_PLAYWRIGHT_TIMEOUT } from '../utils/isomorphic/time' ;
26
- import { existsAsync , removeFolders } from './utils/fileUtils' ;
26
+ import { existsAsync } from './utils/fileUtils' ;
27
27
import { helper } from './helper' ;
28
28
import { SdkObject } from './instrumentation' ;
29
29
import { PipeTransport } from './pipeTransport' ;
@@ -78,14 +78,19 @@ export abstract class BrowserType extends SdkObject {
78
78
// Note: Any initial TLS requests will fail since we rely on the Page/Frames initialize which sets ignoreHTTPSErrors.
79
79
let clientCertificatesProxy : ClientCertificatesProxy | undefined ;
80
80
if ( options . clientCertificates ?. length ) {
81
- clientCertificatesProxy = await progress . raceWithCleanup ( ClientCertificatesProxy . create ( options ) , proxy => proxy . close ( ) ) ;
81
+ clientCertificatesProxy = await ClientCertificatesProxy . create ( progress , options ) ;
82
82
launchOptions . proxyOverride = clientCertificatesProxy . proxySettings ( ) ;
83
83
options = { ...options } ;
84
84
options . internalIgnoreHTTPSErrors = true ;
85
85
}
86
- const browser = await this . _innerLaunchWithRetries ( progress , launchOptions , options , helper . debugProtocolLogger ( ) , userDataDir ) . catch ( e => { throw this . _rewriteStartupLog ( e ) ; } ) ;
87
- browser . _defaultContext ! . _clientCertificatesProxy = clientCertificatesProxy ;
88
- return browser . _defaultContext ! ;
86
+ try {
87
+ const browser = await this . _innerLaunchWithRetries ( progress , launchOptions , options , helper . debugProtocolLogger ( ) , userDataDir ) . catch ( e => { throw this . _rewriteStartupLog ( e ) ; } ) ;
88
+ browser . _defaultContext ! . _clientCertificatesProxy = clientCertificatesProxy ;
89
+ return browser . _defaultContext ! ;
90
+ } catch ( error ) {
91
+ await clientCertificatesProxy ?. close ( ) . catch ( ( ) => { } ) ;
92
+ throw error ;
93
+ }
89
94
}
90
95
91
96
async _innerLaunchWithRetries ( progress : Progress , options : types . LaunchOptions , persistent : types . BrowserContextOptions | undefined , protocolLogger : types . ProtocolLogger , userDataDir ?: string ) : Promise < Browser > {
@@ -106,35 +111,40 @@ export abstract class BrowserType extends SdkObject {
106
111
options . proxy = options . proxy ? normalizeProxySettings ( options . proxy ) : undefined ;
107
112
const browserLogsCollector = new RecentLogsCollector ( ) ;
108
113
const { browserProcess, userDataDir, artifactsDir, transport } = await this . _launchProcess ( progress , options , ! ! persistent , browserLogsCollector , maybeUserDataDir ) ;
109
- if ( ( options as any ) . __testHookBeforeCreateBrowser )
110
- await progress . race ( ( options as any ) . __testHookBeforeCreateBrowser ( ) ) ;
111
- const browserOptions : BrowserOptions = {
112
- name : this . _name ,
113
- isChromium : this . _name === 'chromium' ,
114
- channel : options . channel ,
115
- slowMo : options . slowMo ,
116
- persistent,
117
- headful : ! options . headless ,
118
- artifactsDir,
119
- downloadsPath : ( options . downloadsPath || artifactsDir ) ! ,
120
- tracesDir : ( options . tracesDir || artifactsDir ) ! ,
121
- browserProcess,
122
- customExecutablePath : options . executablePath ,
123
- proxy : options . proxy ,
124
- protocolLogger,
125
- browserLogsCollector,
126
- wsEndpoint : transport instanceof WebSocketTransport ? transport . wsEndpoint : undefined ,
127
- originalLaunchOptions : options ,
128
- } ;
129
- if ( persistent )
130
- validateBrowserContextOptions ( persistent , browserOptions ) ;
131
- copyTestHooks ( options , browserOptions ) ;
132
- const browser = await progress . race ( this . connectToTransport ( transport , browserOptions , browserLogsCollector ) ) ;
133
- ( browser as any ) . _userDataDirForTest = userDataDir ;
134
- // We assume no control when using custom arguments, and do not prepare the default context in that case.
135
- if ( persistent && ! options . ignoreAllDefaultArgs )
136
- await browser . _defaultContext ! . _loadDefaultContext ( progress ) ;
137
- return browser ;
114
+ try {
115
+ if ( ( options as any ) . __testHookBeforeCreateBrowser )
116
+ await progress . race ( ( options as any ) . __testHookBeforeCreateBrowser ( ) ) ;
117
+ const browserOptions : BrowserOptions = {
118
+ name : this . _name ,
119
+ isChromium : this . _name === 'chromium' ,
120
+ channel : options . channel ,
121
+ slowMo : options . slowMo ,
122
+ persistent,
123
+ headful : ! options . headless ,
124
+ artifactsDir,
125
+ downloadsPath : ( options . downloadsPath || artifactsDir ) ! ,
126
+ tracesDir : ( options . tracesDir || artifactsDir ) ! ,
127
+ browserProcess,
128
+ customExecutablePath : options . executablePath ,
129
+ proxy : options . proxy ,
130
+ protocolLogger,
131
+ browserLogsCollector,
132
+ wsEndpoint : transport instanceof WebSocketTransport ? transport . wsEndpoint : undefined ,
133
+ originalLaunchOptions : options ,
134
+ } ;
135
+ if ( persistent )
136
+ validateBrowserContextOptions ( persistent , browserOptions ) ;
137
+ copyTestHooks ( options , browserOptions ) ;
138
+ const browser = await progress . race ( this . connectToTransport ( transport , browserOptions , browserLogsCollector ) ) ;
139
+ ( browser as any ) . _userDataDirForTest = userDataDir ;
140
+ // We assume no control when using custom arguments, and do not prepare the default context in that case.
141
+ if ( persistent && ! options . ignoreAllDefaultArgs )
142
+ await browser . _defaultContext ! . _loadDefaultContext ( progress ) ;
143
+ return browser ;
144
+ } catch ( error ) {
145
+ await browserProcess . close ( ) . catch ( ( ) => { } ) ;
146
+ throw error ;
147
+ }
138
148
}
139
149
140
150
private async _prepareToLaunch ( options : types . LaunchOptions , isPersistent : boolean , userDataDir : string | undefined ) {
@@ -194,7 +204,6 @@ export abstract class BrowserType extends SdkObject {
194
204
195
205
const env = options . env ? envArrayToObject ( options . env ) : process . env ;
196
206
const prepared = await progress . race ( this . _prepareToLaunch ( options , isPersistent , userDataDir ) ) ;
197
- progress . cleanupWhenAborted ( ( ) => removeFolders ( prepared . tempDirectories ) ) ;
198
207
199
208
// Note: it is important to define these variables before launchProcess, so that we don't get
200
209
// "Cannot access 'browserServer' before initialization" if something went wrong.
@@ -217,10 +226,12 @@ export abstract class BrowserType extends SdkObject {
217
226
attemptToGracefullyClose : async ( ) => {
218
227
if ( ( options as any ) . __testHookGracefullyClose )
219
228
await ( options as any ) . __testHookGracefullyClose ( ) ;
220
- // We try to gracefully close to prevent crash reporting and core dumps.
221
- // Note that it's fine to reuse the pipe transport, since
222
- // our connection ignores kBrowserCloseMessageId.
223
- this . attemptToGracefullyCloseBrowser ( transport ! ) ;
229
+ if ( transport ) {
230
+ // We try to gracefully close to prevent crash reporting and core dumps.
231
+ this . attemptToGracefullyCloseBrowser ( transport ) ;
232
+ } else {
233
+ throw new Error ( 'Force-killing the browser because no transport is available to gracefully close it.' ) ;
234
+ }
224
235
} ,
225
236
onExit : ( exitCode , signal ) => {
226
237
// Unblock launch when browser prematurely exits.
@@ -249,19 +260,22 @@ export abstract class BrowserType extends SdkObject {
249
260
close : ( ) => closeOrKill ( ( options as any ) . __testHookBrowserCloseTimeout || DEFAULT_PLAYWRIGHT_TIMEOUT ) ,
250
261
kill
251
262
} ;
252
- progress . cleanupWhenAborted ( ( ) => closeOrKill ( DEFAULT_PLAYWRIGHT_TIMEOUT ) ) ;
253
- const { wsEndpoint } = await progress . race ( [
254
- this . waitForReadyState ( options , browserLogsCollector ) ,
255
- exitPromise . then ( ( ) => ( { wsEndpoint : undefined } ) ) ,
256
- ] ) ;
257
- if ( options . cdpPort !== undefined || ! this . supportsPipeTransport ( ) ) {
258
- transport = await WebSocketTransport . connect ( progress , wsEndpoint ! ) ;
259
- } else {
260
- const stdio = launchedProcess . stdio as unknown as [ NodeJS . ReadableStream , NodeJS . WritableStream , NodeJS . WritableStream , NodeJS . WritableStream , NodeJS . ReadableStream ] ;
261
- transport = new PipeTransport ( stdio [ 3 ] , stdio [ 4 ] ) ;
263
+ try {
264
+ const { wsEndpoint } = await progress . race ( [
265
+ this . waitForReadyState ( options , browserLogsCollector ) ,
266
+ exitPromise . then ( ( ) => ( { wsEndpoint : undefined } ) ) ,
267
+ ] ) ;
268
+ if ( options . cdpPort !== undefined || ! this . supportsPipeTransport ( ) ) {
269
+ transport = await WebSocketTransport . connect ( progress , wsEndpoint ! ) ;
270
+ } else {
271
+ const stdio = launchedProcess . stdio as unknown as [ NodeJS . ReadableStream , NodeJS . WritableStream , NodeJS . WritableStream , NodeJS . WritableStream , NodeJS . ReadableStream ] ;
272
+ transport = new PipeTransport ( stdio [ 3 ] , stdio [ 4 ] ) ;
273
+ }
274
+ return { browserProcess, artifactsDir : prepared . artifactsDir , userDataDir : prepared . userDataDir , transport } ;
275
+ } catch ( error ) {
276
+ await closeOrKill ( DEFAULT_PLAYWRIGHT_TIMEOUT ) . catch ( ( ) => { } ) ;
277
+ throw error ;
262
278
}
263
- progress . cleanupWhenAborted ( ( ) => transport . close ( ) ) ;
264
- return { browserProcess, artifactsDir : prepared . artifactsDir , userDataDir : prepared . userDataDir , transport } ;
265
279
}
266
280
267
281
async _createArtifactDirs ( options : types . LaunchOptions ) : Promise < void > {
0 commit comments