* Recreate the browser context to apply user agent changes. * Saves and restores cookies, localStorage, sessionStorage, and open pages. * Falls back to a clean slate on any failure.
()
| 1394 | * Falls back to a clean slate on any failure. |
| 1395 | */ |
| 1396 | async recreateContext(): Promise<string | null> { |
| 1397 | if (this.connectionMode === 'headed') { |
| 1398 | throw new Error('Cannot recreate context in headed mode. Use disconnect first.'); |
| 1399 | } |
| 1400 | if (!this.browser || !this.context) { |
| 1401 | throw new Error('Browser not launched'); |
| 1402 | } |
| 1403 | |
| 1404 | try { |
| 1405 | // 1. Save state |
| 1406 | const state = await this.saveState(); |
| 1407 | |
| 1408 | // 2. Close old pages and context |
| 1409 | for (const page of this.pages.values()) { |
| 1410 | await page.close().catch(() => {}); |
| 1411 | } |
| 1412 | this.pages.clear(); |
| 1413 | this.tabSessions.clear(); |
| 1414 | await this.context.close().catch(() => {}); |
| 1415 | |
| 1416 | // 3. Create new context with updated settings |
| 1417 | const contextOptions: BrowserContextOptions = { |
| 1418 | viewport: { width: this.currentViewport.width, height: this.currentViewport.height }, |
| 1419 | deviceScaleFactor: this.deviceScaleFactor, |
| 1420 | }; |
| 1421 | if (this.customUserAgent) { |
| 1422 | contextOptions.userAgent = this.customUserAgent; |
| 1423 | } |
| 1424 | this.context = await this.browser.newContext(contextOptions); |
| 1425 | |
| 1426 | // Re-apply stealth: newContext() is a fresh context with no init scripts, |
| 1427 | // so a useragent / viewport --scale rebuild would otherwise drop the |
| 1428 | // webdriver mask, window.chrome.* shape, hardware spoof, and cdc/ |
| 1429 | // Permissions cleanup on every restored page. Must run before |
| 1430 | // restoreState() navigates the restored tabs. |
| 1431 | const { applyStealth } = await import('./stealth'); |
| 1432 | await applyStealth(this.context); |
| 1433 | |
| 1434 | if (Object.keys(this.extraHeaders).length > 0) { |
| 1435 | await this.context.setExtraHTTPHeaders(this.extraHeaders); |
| 1436 | } |
| 1437 | |
| 1438 | // 4. Restore state |
| 1439 | await this.restoreState(state); |
| 1440 | |
| 1441 | return null; // success |
| 1442 | } catch (err: unknown) { |
| 1443 | // Fallback: create a clean context + blank tab |
| 1444 | try { |
| 1445 | this.pages.clear(); |
| 1446 | this.tabSessions.clear(); |
| 1447 | if (this.context) await this.context.close().catch(() => {}); |
| 1448 | |
| 1449 | const contextOptions: BrowserContextOptions = { |
| 1450 | viewport: { width: this.currentViewport.width, height: this.currentViewport.height }, |
| 1451 | deviceScaleFactor: this.deviceScaleFactor, |
| 1452 | }; |
| 1453 | if (this.customUserAgent) { |
no test coverage detected