@@ -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 } from './utils/fileUtils' ;
26
+ import { existsAsync , removeFolders } from './utils/fileUtils' ;
27
27
import { helper } from './helper' ;
28
28
import { SdkObject } from './instrumentation' ;
29
29
import { PipeTransport } from './pipeTransport' ;
@@ -69,7 +69,7 @@ export abstract class BrowserType extends SdkObject {
69
69
70
70
async launch ( metadata : CallMetadata , options : types . LaunchOptions , protocolLogger ?: types . ProtocolLogger ) : Promise < Browser > {
71
71
options = this . _validateLaunchOptions ( options ) ;
72
- const controller = new ProgressController ( metadata , this ) ;
72
+ const controller = new ProgressController ( metadata , this , 'strict' ) ;
73
73
const browser = await controller . run ( progress => {
74
74
const seleniumHubUrl = ( options as any ) . __testHookSeleniumRemoteURL || process . env . SELENIUM_REMOTE_URL ;
75
75
if ( seleniumHubUrl )
@@ -81,17 +81,16 @@ export abstract class BrowserType extends SdkObject {
81
81
82
82
async launchPersistentContext ( metadata : CallMetadata , userDataDir : string , options : channels . BrowserTypeLaunchPersistentContextOptions & { timeout : number , cdpPort ?: number , internalIgnoreHTTPSErrors ?: boolean } ) : Promise < BrowserContext > {
83
83
const launchOptions = this . _validateLaunchOptions ( options ) ;
84
- const controller = new ProgressController ( metadata , this ) ;
84
+ const controller = new ProgressController ( metadata , this , 'strict' ) ;
85
85
const browser = await controller . run ( async progress => {
86
86
// Note: Any initial TLS requests will fail since we rely on the Page/Frames initialize which sets ignoreHTTPSErrors.
87
87
let clientCertificatesProxy : ClientCertificatesProxy | undefined ;
88
88
if ( options . clientCertificates ?. length ) {
89
- clientCertificatesProxy = new ClientCertificatesProxy ( options ) ;
90
- launchOptions . proxyOverride = await clientCertificatesProxy ?. listen ( ) ;
89
+ clientCertificatesProxy = await progress . raceWithCleanup ( ClientCertificatesProxy . create ( options ) , proxy => proxy . close ( ) ) ;
90
+ launchOptions . proxyOverride = clientCertificatesProxy . proxySettings ( ) ;
91
91
options = { ...options } ;
92
92
options . internalIgnoreHTTPSErrors = true ;
93
93
}
94
- progress . cleanupWhenAborted ( ( ) => clientCertificatesProxy ?. close ( ) ) ;
95
94
const browser = await this . _innerLaunchWithRetries ( progress , launchOptions , options , helper . debugProtocolLogger ( ) , userDataDir ) . catch ( e => { throw this . _rewriteStartupLog ( e ) ; } ) ;
96
95
browser . _defaultContext ! . _clientCertificatesProxy = clientCertificatesProxy ;
97
96
return browser ;
@@ -118,7 +117,7 @@ export abstract class BrowserType extends SdkObject {
118
117
const browserLogsCollector = new RecentLogsCollector ( ) ;
119
118
const { browserProcess, userDataDir, artifactsDir, transport } = await this . _launchProcess ( progress , options , ! ! persistent , browserLogsCollector , maybeUserDataDir ) ;
120
119
if ( ( options as any ) . __testHookBeforeCreateBrowser )
121
- await ( options as any ) . __testHookBeforeCreateBrowser ( ) ;
120
+ await progress . race ( ( options as any ) . __testHookBeforeCreateBrowser ( ) ) ;
122
121
const browserOptions : BrowserOptions = {
123
122
name : this . _name ,
124
123
isChromium : this . _name === 'chromium' ,
@@ -140,30 +139,24 @@ export abstract class BrowserType extends SdkObject {
140
139
if ( persistent )
141
140
validateBrowserContextOptions ( persistent , browserOptions ) ;
142
141
copyTestHooks ( options , browserOptions ) ;
143
- const browser = await this . connectToTransport ( transport , browserOptions , browserLogsCollector ) ;
142
+ const browser = await progress . race ( this . connectToTransport ( transport , browserOptions , browserLogsCollector ) ) ;
144
143
( browser as any ) . _userDataDirForTest = userDataDir ;
145
144
// We assume no control when using custom arguments, and do not prepare the default context in that case.
146
145
if ( persistent && ! options . ignoreAllDefaultArgs )
147
146
await browser . _defaultContext ! . _loadDefaultContext ( progress ) ;
148
147
return browser ;
149
148
}
150
149
151
- private async _launchProcess ( progress : Progress , options : types . LaunchOptions , isPersistent : boolean , browserLogsCollector : RecentLogsCollector , userDataDir ? : string ) : Promise < { browserProcess : BrowserProcess , artifactsDir : string , userDataDir : string , transport : ConnectionTransport } > {
150
+ private async _prepareToLaunch ( options : types . LaunchOptions , isPersistent : boolean , userDataDir : string | undefined ) {
152
151
const {
153
152
ignoreDefaultArgs,
154
153
ignoreAllDefaultArgs,
155
154
args = [ ] ,
156
155
executablePath = null ,
157
- handleSIGINT = true ,
158
- handleSIGTERM = true ,
159
- handleSIGHUP = true ,
160
156
} = options ;
161
-
162
- const env = options . env ? envArrayToObject ( options . env ) : process . env ;
163
-
164
157
await this . _createArtifactDirs ( options ) ;
165
158
166
- const tempDirectories = [ ] ;
159
+ const tempDirectories : string [ ] = [ ] ;
167
160
const artifactsDir = await fs . promises . mkdtemp ( path . join ( os . tmpdir ( ) , 'playwright-artifacts-' ) ) ;
168
161
tempDirectories . push ( artifactsDir ) ;
169
162
@@ -178,7 +171,7 @@ export abstract class BrowserType extends SdkObject {
178
171
}
179
172
await this . prepareUserDataDir ( options , userDataDir ) ;
180
173
181
- const browserArguments = [ ] ;
174
+ const browserArguments : string [ ] = [ ] ;
182
175
if ( ignoreAllDefaultArgs )
183
176
browserArguments . push ( ...args ) ;
184
177
else if ( ignoreDefaultArgs )
@@ -199,15 +192,29 @@ export abstract class BrowserType extends SdkObject {
199
192
await registry . validateHostRequirementsForExecutablesIfNeeded ( [ registryExecutable ] , this . attribution . playwright . options . sdkLanguage ) ;
200
193
}
201
194
195
+ return { executable, browserArguments, userDataDir, artifactsDir, tempDirectories } ;
196
+ }
197
+
198
+ private async _launchProcess ( progress : Progress , options : types . LaunchOptions , isPersistent : boolean , browserLogsCollector : RecentLogsCollector , userDataDir ?: string ) : Promise < { browserProcess : BrowserProcess , artifactsDir : string , userDataDir : string , transport : ConnectionTransport } > {
199
+ const {
200
+ handleSIGINT = true ,
201
+ handleSIGTERM = true ,
202
+ handleSIGHUP = true ,
203
+ } = options ;
204
+
205
+ const env = options . env ? envArrayToObject ( options . env ) : process . env ;
206
+ const prepared = await progress . race ( this . _prepareToLaunch ( options , isPersistent , userDataDir ) ) ;
207
+ progress . cleanupWhenAborted ( ( ) => removeFolders ( prepared . tempDirectories ) ) ;
208
+
202
209
// Note: it is important to define these variables before launchProcess, so that we don't get
203
210
// "Cannot access 'browserServer' before initialization" if something went wrong.
204
211
let transport : ConnectionTransport | undefined = undefined ;
205
212
let browserProcess : BrowserProcess | undefined = undefined ;
206
213
const exitPromise = new ManualPromise ( ) ;
207
214
const { launchedProcess, gracefullyClose, kill } = await launchProcess ( {
208
- command : executable ,
209
- args : browserArguments ,
210
- env : this . amendEnvironment ( env , userDataDir , executable , browserArguments ) ,
215
+ command : prepared . executable ,
216
+ args : prepared . browserArguments ,
217
+ env : this . amendEnvironment ( env , prepared . userDataDir , prepared . executable , prepared . browserArguments ) ,
211
218
handleSIGINT,
212
219
handleSIGTERM,
213
220
handleSIGHUP,
@@ -216,7 +223,7 @@ export abstract class BrowserType extends SdkObject {
216
223
browserLogsCollector . log ( message ) ;
217
224
} ,
218
225
stdio : 'pipe' ,
219
- tempDirectories,
226
+ tempDirectories : prepared . tempDirectories ,
220
227
attemptToGracefullyClose : async ( ) => {
221
228
if ( ( options as any ) . __testHookGracefullyClose )
222
229
await ( options as any ) . __testHookGracefullyClose ( ) ;
@@ -253,7 +260,7 @@ export abstract class BrowserType extends SdkObject {
253
260
kill
254
261
} ;
255
262
progress . cleanupWhenAborted ( ( ) => closeOrKill ( progress . timeUntilDeadline ( ) ) ) ;
256
- const { wsEndpoint } = await Promise . race ( [
263
+ const { wsEndpoint } = await progress . race ( [
257
264
this . waitForReadyState ( options , browserLogsCollector ) ,
258
265
exitPromise . then ( ( ) => ( { wsEndpoint : undefined } ) ) ,
259
266
] ) ;
@@ -263,7 +270,8 @@ export abstract class BrowserType extends SdkObject {
263
270
const stdio = launchedProcess . stdio as unknown as [ NodeJS . ReadableStream , NodeJS . WritableStream , NodeJS . WritableStream , NodeJS . WritableStream , NodeJS . ReadableStream ] ;
264
271
transport = new PipeTransport ( stdio [ 3 ] , stdio [ 4 ] ) ;
265
272
}
266
- return { browserProcess, artifactsDir, userDataDir, transport } ;
273
+ progress . cleanupWhenAborted ( ( ) => transport . close ( ) ) ;
274
+ return { browserProcess, artifactsDir : prepared . artifactsDir , userDataDir : prepared . userDataDir , transport } ;
267
275
}
268
276
269
277
async _createArtifactDirs ( options : types . LaunchOptions ) : Promise < void > {
0 commit comments