(
chromeArgs: string[],
config?: Partial<
Pick<
EngineConfig,
"browserTimeout" | "protocolTimeout" | "enableBrowserPool" | "chromePath" | "forceScreenshot"
>
>,
)
| 354 | } |
| 355 | |
| 356 | export async function acquireBrowser( |
| 357 | chromeArgs: string[], |
| 358 | config?: Partial< |
| 359 | Pick< |
| 360 | EngineConfig, |
| 361 | "browserTimeout" | "protocolTimeout" | "enableBrowserPool" | "chromePath" | "forceScreenshot" |
| 362 | > |
| 363 | >, |
| 364 | ): Promise<AcquiredBrowser> { |
| 365 | const enablePool = config?.enableBrowserPool ?? DEFAULT_CONFIG.enableBrowserPool; |
| 366 | |
| 367 | if (enablePool && pooledBrowser) { |
| 368 | if (!pooledBrowser.connected) { |
| 369 | pooledBrowser = null; |
| 370 | pooledBrowserRefCount = 0; |
| 371 | _pooledBrowserLaunchPromise = null; |
| 372 | } else { |
| 373 | // Validate mode compatibility: a caller that needs screenshot mode |
| 374 | // (forceScreenshot, alpha output, BeginFrame timeout retry) must not |
| 375 | // receive a beginframe browser — the BeginFrame-only flags make the |
| 376 | // compositor wait for frames the screenshot path never sends. |
| 377 | const requestedMode = resolveRequestedCaptureMode(config); |
| 378 | if (pooledCaptureMode === requestedMode) { |
| 379 | pooledBrowserRefCount += 1; |
| 380 | return { browser: pooledBrowser, captureMode: pooledCaptureMode }; |
| 381 | } |
| 382 | // Mode mismatch — skip pool, launch a dedicated browser for this caller. |
| 383 | // Don't evict the pooled browser: other sessions may still hold refs. |
| 384 | } |
| 385 | } |
| 386 | |
| 387 | // Dedup concurrent launches: when the pool is enabled and multiple callers |
| 388 | // (e.g. parallel workers via Promise.all) race into acquireBrowser before |
| 389 | // the first launch completes, they would all see pooledBrowser === null and |
| 390 | // each spawn a separate Chrome. Cache the in-flight launch Promise so the |
| 391 | // second+ callers await the same one instead of launching again. |
| 392 | if (enablePool && _pooledBrowserLaunchPromise) { |
| 393 | const result = await _pooledBrowserLaunchPromise; |
| 394 | const requestedMode = resolveRequestedCaptureMode(config); |
| 395 | if (result.captureMode === requestedMode) { |
| 396 | pooledBrowserRefCount += 1; |
| 397 | return result; |
| 398 | } |
| 399 | // Mode mismatch with pending launch — launch a dedicated browser. |
| 400 | } |
| 401 | |
| 402 | const launchPromise = launchBrowser(chromeArgs, config); |
| 403 | |
| 404 | if (enablePool && !pooledBrowser && !_pooledBrowserLaunchPromise) { |
| 405 | _pooledBrowserLaunchPromise = launchPromise; |
| 406 | try { |
| 407 | const result = await launchPromise; |
| 408 | pooledBrowser = result.browser; |
| 409 | pooledBrowserRefCount = 1; |
| 410 | pooledCaptureMode = result.captureMode; |
| 411 | return result; |
| 412 | } finally { |
| 413 | _pooledBrowserLaunchPromise = null; |
no test coverage detected