@push.rocks/smartpuppeteer 2.5.0 → 2.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/smartpuppeteer.classes.livebrowsersession.d.ts +13 -0
- package/dist_ts/smartpuppeteer.classes.livebrowsersession.js +352 -39
- package/package.json +1 -1
- package/readme.hints.md +2 -0
- package/readme.md +1 -1
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/smartpuppeteer.classes.livebrowsersession.ts +509 -41
|
@@ -82,10 +82,12 @@ const maxProxySecuritySessions = 256;
|
|
|
82
82
|
const maxTrackedProxyRequests = 1024;
|
|
83
83
|
const maxTotalTrackedProxyRequests = 4096;
|
|
84
84
|
const maxProxySecurityOperations = 2048;
|
|
85
|
+
const maxTrackedServiceWorkerVersions = 512;
|
|
86
|
+
const proxySecurityCommandTimeoutMs = 5000;
|
|
87
|
+
const proxySecurityCommandOptions = { timeout: proxySecurityCommandTimeoutMs } as const;
|
|
85
88
|
const proxySeedTargetTypes = new Set([
|
|
86
89
|
'background_page',
|
|
87
90
|
'page',
|
|
88
|
-
'service_worker',
|
|
89
91
|
'shared_worker',
|
|
90
92
|
'webview',
|
|
91
93
|
]);
|
|
@@ -99,6 +101,9 @@ type TProxyAuthRequiredEvent = plugins.puppeteer.Protocol.Fetch.AuthRequiredEven
|
|
|
99
101
|
type TProxyRequestPausedEvent = plugins.puppeteer.Protocol.Fetch.RequestPausedEvent;
|
|
100
102
|
type TNetworkLoadingFinishedEvent = plugins.puppeteer.Protocol.Network.LoadingFinishedEvent;
|
|
101
103
|
type TNetworkLoadingFailedEvent = plugins.puppeteer.Protocol.Network.LoadingFailedEvent;
|
|
104
|
+
type TServiceWorkerVersionUpdatedEvent = (
|
|
105
|
+
plugins.puppeteer.Protocol.ServiceWorker.WorkerVersionUpdatedEvent
|
|
106
|
+
);
|
|
102
107
|
|
|
103
108
|
interface IProxySecuritySession {
|
|
104
109
|
session: plugins.puppeteer.CDPSession;
|
|
@@ -108,6 +113,7 @@ interface IProxySecuritySession {
|
|
|
108
113
|
fetchEnabled: boolean;
|
|
109
114
|
allowLiveTargetDetach: boolean;
|
|
110
115
|
ownedByProxySecurity: boolean;
|
|
116
|
+
retireWhenIdle: boolean;
|
|
111
117
|
attemptedAuthentications: Set<string>;
|
|
112
118
|
requestIdsByNetworkId: Map<string, string>;
|
|
113
119
|
authRequiredListener: (event: TProxyAuthRequiredEvent) => void;
|
|
@@ -146,7 +152,8 @@ interface IPrivateLiveBrowserTab {
|
|
|
146
152
|
closing: boolean;
|
|
147
153
|
stateUpdateQueued: boolean;
|
|
148
154
|
stateUpdatePending: boolean;
|
|
149
|
-
|
|
155
|
+
navigationResetRevision: number;
|
|
156
|
+
restoredNavigationRevision: number;
|
|
150
157
|
evaluationExecutionContextId?: number;
|
|
151
158
|
securityCdpSession?: plugins.puppeteer.CDPSession;
|
|
152
159
|
cdpSession?: plugins.puppeteer.CDPSession;
|
|
@@ -396,8 +403,18 @@ export class LiveBrowserSession {
|
|
|
396
403
|
private browserSecurityCdpSession?: plugins.puppeteer.CDPSession;
|
|
397
404
|
private browserSecurityConnection?: plugins.puppeteer.Connection;
|
|
398
405
|
private browserSecuritySessionAttachedListener?: (session: plugins.puppeteer.CDPSession) => void;
|
|
406
|
+
private browserSecurityOwnedSessionAttachedListener?: (
|
|
407
|
+
session: plugins.puppeteer.CDPSession,
|
|
408
|
+
) => void;
|
|
399
409
|
private browserSecuritySessionDetachedListener?: TCdpSessionDetachedListener;
|
|
410
|
+
private browserSecurityWorkerLifecycleCdpSession?: plugins.puppeteer.CDPSession;
|
|
411
|
+
private browserSecurityWorkerVersionUpdatedListener?: (
|
|
412
|
+
event: TServiceWorkerVersionUpdatedEvent,
|
|
413
|
+
) => void;
|
|
400
414
|
private readonly proxySecuritySessions = new Map<string, IProxySecuritySession>();
|
|
415
|
+
private readonly ownedProxySecuritySessions = new Set<plugins.puppeteer.CDPSession>();
|
|
416
|
+
private readonly browserSecurityParentOwnedSessions = new Set<plugins.puppeteer.CDPSession>();
|
|
417
|
+
private readonly serviceWorkerTargetIdsByVersionId = new Map<string, string>();
|
|
401
418
|
private readonly proxySecurityOperations = new Set<Promise<void>>();
|
|
402
419
|
private proxySecurityGeneration = 0;
|
|
403
420
|
private proxySecurityStopping = false;
|
|
@@ -2276,7 +2293,10 @@ export class LiveBrowserSession {
|
|
|
2276
2293
|
tab.securityCdpSession = undefined;
|
|
2277
2294
|
if (securityCdpSession && !securityCdpSession.detached) {
|
|
2278
2295
|
try {
|
|
2279
|
-
await
|
|
2296
|
+
await this.detachCdpSessionWithTimeout(
|
|
2297
|
+
securityCdpSession,
|
|
2298
|
+
'page security session',
|
|
2299
|
+
);
|
|
2280
2300
|
} catch {
|
|
2281
2301
|
// Browser lifetime cancellation may close the target before explicit detach settles.
|
|
2282
2302
|
}
|
|
@@ -2346,11 +2366,18 @@ export class LiveBrowserSession {
|
|
|
2346
2366
|
if (!proxyCredentials) {
|
|
2347
2367
|
try {
|
|
2348
2368
|
if (denyPermissions) {
|
|
2349
|
-
await cdpSession.send(
|
|
2369
|
+
await cdpSession.send(
|
|
2370
|
+
'Browser.grantPermissions',
|
|
2371
|
+
{ permissions: [] },
|
|
2372
|
+
proxySecurityCommandOptions,
|
|
2373
|
+
);
|
|
2350
2374
|
}
|
|
2351
2375
|
} finally {
|
|
2352
2376
|
if (!cdpSession.detached) {
|
|
2353
|
-
await
|
|
2377
|
+
await this.detachCdpSessionWithTimeout(
|
|
2378
|
+
cdpSession,
|
|
2379
|
+
'browser permission session',
|
|
2380
|
+
);
|
|
2354
2381
|
}
|
|
2355
2382
|
}
|
|
2356
2383
|
return;
|
|
@@ -2365,7 +2392,11 @@ export class LiveBrowserSession {
|
|
|
2365
2392
|
this.browserSecurityCdpSession = cdpSession;
|
|
2366
2393
|
try {
|
|
2367
2394
|
if (denyPermissions) {
|
|
2368
|
-
await cdpSession.send(
|
|
2395
|
+
await cdpSession.send(
|
|
2396
|
+
'Browser.grantPermissions',
|
|
2397
|
+
{ permissions: [] },
|
|
2398
|
+
proxySecurityCommandOptions,
|
|
2399
|
+
);
|
|
2369
2400
|
}
|
|
2370
2401
|
const browserSecurityConnection = cdpSession.connection();
|
|
2371
2402
|
if (!browserSecurityConnection) {
|
|
@@ -2373,13 +2404,69 @@ export class LiveBrowserSession {
|
|
|
2373
2404
|
}
|
|
2374
2405
|
this.browserSecurityConnection = browserSecurityConnection;
|
|
2375
2406
|
this.browserSecuritySessionAttachedListener = (attachedSession) => {
|
|
2407
|
+
const setupPromise = this.configureProxySecuritySession(attachedSession, generation);
|
|
2408
|
+
queueMicrotask(() => {
|
|
2409
|
+
const securityRecord = this.proxySecuritySessions.get(attachedSession.id());
|
|
2410
|
+
if (
|
|
2411
|
+
securityRecord?.generation === generation
|
|
2412
|
+
&& securityRecord.ownedByProxySecurity
|
|
2413
|
+
) {
|
|
2414
|
+
return;
|
|
2415
|
+
}
|
|
2416
|
+
this.trackProxySecurityOperation(
|
|
2417
|
+
setupPromise,
|
|
2418
|
+
generation,
|
|
2419
|
+
'proxy_security_session_setup_failed',
|
|
2420
|
+
);
|
|
2421
|
+
});
|
|
2422
|
+
};
|
|
2423
|
+
this.browserSecurityOwnedSessionAttachedListener = (attachedSession) => {
|
|
2424
|
+
const securityRecord = this.proxySecuritySessions.get(attachedSession.id());
|
|
2425
|
+
if (!securityRecord || securityRecord.generation !== generation) {
|
|
2426
|
+
return;
|
|
2427
|
+
}
|
|
2428
|
+
securityRecord.ownedByProxySecurity = true;
|
|
2429
|
+
this.ownedProxySecuritySessions.add(attachedSession);
|
|
2430
|
+
this.browserSecurityParentOwnedSessions.add(attachedSession);
|
|
2431
|
+
const setupPromise = securityRecord.setupPromise;
|
|
2432
|
+
securityRecord.setupPromise = setupPromise.then(async () => {
|
|
2433
|
+
try {
|
|
2434
|
+
await attachedSession.send(
|
|
2435
|
+
'Runtime.runIfWaitingForDebugger',
|
|
2436
|
+
undefined,
|
|
2437
|
+
proxySecurityCommandOptions,
|
|
2438
|
+
);
|
|
2439
|
+
} catch (error) {
|
|
2440
|
+
this.removeProxySecuritySession(securityRecord);
|
|
2441
|
+
if (attachedSession.detached) {
|
|
2442
|
+
this.forgetOwnedProxySecuritySession(attachedSession);
|
|
2443
|
+
return;
|
|
2444
|
+
}
|
|
2445
|
+
if (this.proxySecurityStopping || this.normalStopRequested) {
|
|
2446
|
+
await this.detachOwnedProxySecuritySession(securityRecord);
|
|
2447
|
+
return;
|
|
2448
|
+
}
|
|
2449
|
+
if (
|
|
2450
|
+
securityRecord.targetId
|
|
2451
|
+
&& await this.waitForProxySecurityTargetCoverage(
|
|
2452
|
+
securityRecord.targetId,
|
|
2453
|
+
generation,
|
|
2454
|
+
)
|
|
2455
|
+
) {
|
|
2456
|
+
await this.detachOwnedProxySecuritySession(securityRecord);
|
|
2457
|
+
return;
|
|
2458
|
+
}
|
|
2459
|
+
throw error;
|
|
2460
|
+
}
|
|
2461
|
+
});
|
|
2376
2462
|
this.trackProxySecurityOperation(
|
|
2377
|
-
|
|
2463
|
+
securityRecord.setupPromise,
|
|
2378
2464
|
generation,
|
|
2379
2465
|
'proxy_security_session_setup_failed',
|
|
2380
2466
|
);
|
|
2381
2467
|
};
|
|
2382
2468
|
this.browserSecuritySessionDetachedListener = (detachedSession) => {
|
|
2469
|
+
this.forgetOwnedProxySecuritySession(detachedSession);
|
|
2383
2470
|
if (detachedSession === cdpSession) {
|
|
2384
2471
|
this.handleProxySecurityFailure(
|
|
2385
2472
|
'proxy_security_browser_session_detached',
|
|
@@ -2388,6 +2475,14 @@ export class LiveBrowserSession {
|
|
|
2388
2475
|
);
|
|
2389
2476
|
return;
|
|
2390
2477
|
}
|
|
2478
|
+
if (detachedSession === this.browserSecurityWorkerLifecycleCdpSession) {
|
|
2479
|
+
this.handleProxySecurityFailure(
|
|
2480
|
+
'proxy_security_worker_lifecycle_detached',
|
|
2481
|
+
new Error('Proxy security service-worker lifecycle session detached unexpectedly'),
|
|
2482
|
+
generation,
|
|
2483
|
+
);
|
|
2484
|
+
return;
|
|
2485
|
+
}
|
|
2391
2486
|
this.trackProxySecurityOperation(
|
|
2392
2487
|
this.verifyProxySecuritySessionDetached(detachedSession, generation),
|
|
2393
2488
|
generation,
|
|
@@ -2398,11 +2493,30 @@ export class LiveBrowserSession {
|
|
|
2398
2493
|
'sessionattached',
|
|
2399
2494
|
this.browserSecuritySessionAttachedListener,
|
|
2400
2495
|
);
|
|
2496
|
+
cdpSession.on(
|
|
2497
|
+
plugins.puppeteer.CDPSessionEvent.SessionAttached,
|
|
2498
|
+
this.browserSecurityOwnedSessionAttachedListener,
|
|
2499
|
+
);
|
|
2401
2500
|
browserSecurityConnection.on(
|
|
2402
2501
|
'sessiondetached',
|
|
2403
2502
|
this.browserSecuritySessionDetachedListener,
|
|
2404
2503
|
);
|
|
2405
2504
|
|
|
2505
|
+
await cdpSession.send('Target.setAutoAttach', {
|
|
2506
|
+
autoAttach: true,
|
|
2507
|
+
waitForDebuggerOnStart: true,
|
|
2508
|
+
flatten: true,
|
|
2509
|
+
filter: [
|
|
2510
|
+
{ type: 'service_worker' },
|
|
2511
|
+
{ exclude: true },
|
|
2512
|
+
],
|
|
2513
|
+
}, proxySecurityCommandOptions);
|
|
2514
|
+
await Promise.all(
|
|
2515
|
+
[...this.proxySecuritySessions.values()]
|
|
2516
|
+
.filter((record) => record.generation === generation && record.ownedByProxySecurity)
|
|
2517
|
+
.map((record) => record.setupPromise),
|
|
2518
|
+
);
|
|
2519
|
+
|
|
2406
2520
|
for (const target of browser.targets()) {
|
|
2407
2521
|
if (!proxySeedTargetTypes.has(target.type())) {
|
|
2408
2522
|
continue;
|
|
@@ -2413,14 +2527,121 @@ export class LiveBrowserSession {
|
|
|
2413
2527
|
throw new Error(`Proxy security did not observe session ${securitySession.id()}`);
|
|
2414
2528
|
}
|
|
2415
2529
|
securityRecord.ownedByProxySecurity = true;
|
|
2530
|
+
this.ownedProxySecuritySessions.add(securitySession);
|
|
2416
2531
|
await securityRecord.setupPromise;
|
|
2417
2532
|
}
|
|
2533
|
+
await this.configureProxySecurityWorkerLifecycle(cdpSession, generation);
|
|
2418
2534
|
} catch (error) {
|
|
2419
2535
|
await this.teardownProxySecurity();
|
|
2420
2536
|
throw error;
|
|
2421
2537
|
}
|
|
2422
2538
|
}
|
|
2423
2539
|
|
|
2540
|
+
private async configureProxySecurityWorkerLifecycle(
|
|
2541
|
+
browserSecurityCdpSession: plugins.puppeteer.CDPSession,
|
|
2542
|
+
generation: number,
|
|
2543
|
+
): Promise<void> {
|
|
2544
|
+
const lifecycleUrl = `data:text/html,<title>smartpuppeteer-proxy-security-${generation}</title>`;
|
|
2545
|
+
const { targetId } = await browserSecurityCdpSession.send('Target.createTarget', {
|
|
2546
|
+
url: lifecycleUrl,
|
|
2547
|
+
background: true,
|
|
2548
|
+
hidden: true,
|
|
2549
|
+
}, proxySecurityCommandOptions);
|
|
2550
|
+
const attachedSessions = new Map<string, plugins.puppeteer.CDPSession>();
|
|
2551
|
+
const captureAttachedSession = (session: plugins.puppeteer.CDPSession): void => {
|
|
2552
|
+
attachedSessions.set(session.id(), session);
|
|
2553
|
+
};
|
|
2554
|
+
browserSecurityCdpSession.on(
|
|
2555
|
+
plugins.puppeteer.CDPSessionEvent.SessionAttached,
|
|
2556
|
+
captureAttachedSession,
|
|
2557
|
+
);
|
|
2558
|
+
let lifecycleSession: plugins.puppeteer.CDPSession;
|
|
2559
|
+
try {
|
|
2560
|
+
const { sessionId } = await browserSecurityCdpSession.send('Target.attachToTarget', {
|
|
2561
|
+
targetId,
|
|
2562
|
+
flatten: true,
|
|
2563
|
+
}, proxySecurityCommandOptions);
|
|
2564
|
+
const attachedSession = attachedSessions.get(sessionId);
|
|
2565
|
+
if (!attachedSession) {
|
|
2566
|
+
throw new Error('Proxy security did not observe its hidden lifecycle session');
|
|
2567
|
+
}
|
|
2568
|
+
lifecycleSession = attachedSession;
|
|
2569
|
+
} finally {
|
|
2570
|
+
browserSecurityCdpSession.off(
|
|
2571
|
+
plugins.puppeteer.CDPSessionEvent.SessionAttached,
|
|
2572
|
+
captureAttachedSession,
|
|
2573
|
+
);
|
|
2574
|
+
}
|
|
2575
|
+
|
|
2576
|
+
const securityRecord = this.proxySecuritySessions.get(lifecycleSession.id());
|
|
2577
|
+
if (!securityRecord || !securityRecord.ownedByProxySecurity) {
|
|
2578
|
+
throw new Error('Proxy security did not configure its hidden lifecycle session');
|
|
2579
|
+
}
|
|
2580
|
+
await securityRecord.setupPromise;
|
|
2581
|
+
if (lifecycleSession.detached) {
|
|
2582
|
+
throw new Error('Proxy security hidden lifecycle session detached during setup');
|
|
2583
|
+
}
|
|
2584
|
+
|
|
2585
|
+
this.browserSecurityWorkerLifecycleCdpSession = lifecycleSession;
|
|
2586
|
+
this.browserSecurityWorkerVersionUpdatedListener = (event) => {
|
|
2587
|
+
const stoppedTargetIds = new Set<string>();
|
|
2588
|
+
const redundantTargetIds = new Set<string>();
|
|
2589
|
+
for (const version of event.versions) {
|
|
2590
|
+
if (version.targetId) {
|
|
2591
|
+
if (
|
|
2592
|
+
!this.serviceWorkerTargetIdsByVersionId.has(version.versionId)
|
|
2593
|
+
&& this.serviceWorkerTargetIdsByVersionId.size >= maxTrackedServiceWorkerVersions
|
|
2594
|
+
) {
|
|
2595
|
+
this.handleProxySecurityFailure(
|
|
2596
|
+
'proxy_security_worker_tracking_capacity_exceeded',
|
|
2597
|
+
new Error(
|
|
2598
|
+
`Proxy security exceeded ${maxTrackedServiceWorkerVersions} service-worker versions`,
|
|
2599
|
+
),
|
|
2600
|
+
generation,
|
|
2601
|
+
);
|
|
2602
|
+
return;
|
|
2603
|
+
}
|
|
2604
|
+
this.serviceWorkerTargetIdsByVersionId.set(version.versionId, version.targetId);
|
|
2605
|
+
}
|
|
2606
|
+
const knownTargetId = version.targetId
|
|
2607
|
+
?? this.serviceWorkerTargetIdsByVersionId.get(version.versionId);
|
|
2608
|
+
if (version.status === 'redundant' && knownTargetId) {
|
|
2609
|
+
redundantTargetIds.add(knownTargetId);
|
|
2610
|
+
}
|
|
2611
|
+
if (version.runningStatus === 'stopped') {
|
|
2612
|
+
if (knownTargetId) {
|
|
2613
|
+
stoppedTargetIds.add(knownTargetId);
|
|
2614
|
+
}
|
|
2615
|
+
this.serviceWorkerTargetIdsByVersionId.delete(version.versionId);
|
|
2616
|
+
}
|
|
2617
|
+
}
|
|
2618
|
+
for (const workerSecurityRecord of [...this.proxySecuritySessions.values()]) {
|
|
2619
|
+
if (
|
|
2620
|
+
!workerSecurityRecord.ownedByProxySecurity
|
|
2621
|
+
|| workerSecurityRecord.targetType !== 'service_worker'
|
|
2622
|
+
|| !workerSecurityRecord.targetId
|
|
2623
|
+
|| !this.browserSecurityParentOwnedSessions.has(workerSecurityRecord.session)
|
|
2624
|
+
) {
|
|
2625
|
+
continue;
|
|
2626
|
+
}
|
|
2627
|
+
if (stoppedTargetIds.has(workerSecurityRecord.targetId)) {
|
|
2628
|
+
this.retireOwnedProxySecuritySession(workerSecurityRecord, true);
|
|
2629
|
+
} else if (redundantTargetIds.has(workerSecurityRecord.targetId)) {
|
|
2630
|
+
this.retireOwnedProxySecuritySession(workerSecurityRecord, false);
|
|
2631
|
+
}
|
|
2632
|
+
}
|
|
2633
|
+
};
|
|
2634
|
+
lifecycleSession.on(
|
|
2635
|
+
'ServiceWorker.workerVersionUpdated',
|
|
2636
|
+
this.browserSecurityWorkerVersionUpdatedListener,
|
|
2637
|
+
);
|
|
2638
|
+
await lifecycleSession.send(
|
|
2639
|
+
'ServiceWorker.enable',
|
|
2640
|
+
undefined,
|
|
2641
|
+
proxySecurityCommandOptions,
|
|
2642
|
+
);
|
|
2643
|
+
}
|
|
2644
|
+
|
|
2424
2645
|
private configureProxySecuritySession(
|
|
2425
2646
|
session: plugins.puppeteer.CDPSession,
|
|
2426
2647
|
generation: number,
|
|
@@ -2483,7 +2704,7 @@ export class LiveBrowserSession {
|
|
|
2483
2704
|
session.send('Fetch.continueWithAuth', {
|
|
2484
2705
|
requestId: event.requestId,
|
|
2485
2706
|
authChallengeResponse,
|
|
2486
|
-
}).then(() => undefined),
|
|
2707
|
+
}, proxySecurityCommandOptions).then(() => undefined),
|
|
2487
2708
|
generation,
|
|
2488
2709
|
'proxy_security_protocol_failed',
|
|
2489
2710
|
);
|
|
@@ -2516,7 +2737,11 @@ export class LiveBrowserSession {
|
|
|
2516
2737
|
}
|
|
2517
2738
|
}
|
|
2518
2739
|
this.trackProxySecurityOperation(
|
|
2519
|
-
session.send(
|
|
2740
|
+
session.send(
|
|
2741
|
+
'Fetch.continueRequest',
|
|
2742
|
+
{ requestId: event.requestId },
|
|
2743
|
+
proxySecurityCommandOptions,
|
|
2744
|
+
).then(() => undefined),
|
|
2520
2745
|
generation,
|
|
2521
2746
|
'proxy_security_protocol_failed',
|
|
2522
2747
|
);
|
|
@@ -2528,6 +2753,13 @@ export class LiveBrowserSession {
|
|
|
2528
2753
|
}
|
|
2529
2754
|
requestIdsByNetworkId.delete(networkId);
|
|
2530
2755
|
attemptedAuthentications.delete(requestId);
|
|
2756
|
+
if (
|
|
2757
|
+
proxySecuritySession.retireWhenIdle
|
|
2758
|
+
&& attemptedAuthentications.size === 0
|
|
2759
|
+
&& requestIdsByNetworkId.size === 0
|
|
2760
|
+
) {
|
|
2761
|
+
this.retireOwnedProxySecuritySession(proxySecuritySession, false);
|
|
2762
|
+
}
|
|
2531
2763
|
};
|
|
2532
2764
|
const loadingFinishedListener = (event: TNetworkLoadingFinishedEvent): void => {
|
|
2533
2765
|
forgetAuthentication(event.requestId);
|
|
@@ -2546,6 +2778,7 @@ export class LiveBrowserSession {
|
|
|
2546
2778
|
fetchEnabled: false,
|
|
2547
2779
|
allowLiveTargetDetach: false,
|
|
2548
2780
|
ownedByProxySecurity: false,
|
|
2781
|
+
retireWhenIdle: false,
|
|
2549
2782
|
attemptedAuthentications,
|
|
2550
2783
|
requestIdsByNetworkId,
|
|
2551
2784
|
authRequiredListener,
|
|
@@ -2554,12 +2787,20 @@ export class LiveBrowserSession {
|
|
|
2554
2787
|
loadingFailedListener,
|
|
2555
2788
|
setupPromise: Promise.resolve(),
|
|
2556
2789
|
};
|
|
2557
|
-
const targetInfoPromise = session.send(
|
|
2558
|
-
|
|
2790
|
+
const targetInfoPromise = session.send(
|
|
2791
|
+
'Target.getTargetInfo',
|
|
2792
|
+
undefined,
|
|
2793
|
+
proxySecurityCommandOptions,
|
|
2794
|
+
);
|
|
2795
|
+
const networkEnablePromise = session.send(
|
|
2796
|
+
'Network.enable',
|
|
2797
|
+
undefined,
|
|
2798
|
+
proxySecurityCommandOptions,
|
|
2799
|
+
);
|
|
2559
2800
|
const fetchEnablePromise = session.send('Fetch.enable', {
|
|
2560
2801
|
handleAuthRequests: true,
|
|
2561
2802
|
patterns: [{ urlPattern: '*' }],
|
|
2562
|
-
});
|
|
2803
|
+
}, proxySecurityCommandOptions);
|
|
2563
2804
|
const commandResultsPromise = Promise.allSettled([
|
|
2564
2805
|
targetInfoPromise,
|
|
2565
2806
|
networkEnablePromise,
|
|
@@ -2590,10 +2831,28 @@ export class LiveBrowserSession {
|
|
|
2590
2831
|
proxySecuritySession.fetchEnabled = true;
|
|
2591
2832
|
} catch (error) {
|
|
2592
2833
|
this.removeProxySecuritySession(proxySecuritySession);
|
|
2593
|
-
if (session.detached
|
|
2834
|
+
if (session.detached) {
|
|
2835
|
+
this.forgetOwnedProxySecuritySession(session);
|
|
2594
2836
|
return;
|
|
2595
2837
|
}
|
|
2596
|
-
|
|
2838
|
+
if (this.proxySecurityStopping || this.normalStopRequested) {
|
|
2839
|
+
await this.detachOwnedProxySecuritySession(proxySecuritySession);
|
|
2840
|
+
return;
|
|
2841
|
+
}
|
|
2842
|
+
if (
|
|
2843
|
+
proxySecuritySession.targetId
|
|
2844
|
+
&& await this.waitForProxySecurityTargetCoverage(
|
|
2845
|
+
proxySecuritySession.targetId,
|
|
2846
|
+
generation,
|
|
2847
|
+
)
|
|
2848
|
+
) {
|
|
2849
|
+
await this.detachOwnedProxySecuritySession(proxySecuritySession);
|
|
2850
|
+
return;
|
|
2851
|
+
}
|
|
2852
|
+
throw new Error(
|
|
2853
|
+
`Proxy security ${proxySecuritySession.ownedByProxySecurity ? 'owned' : 'observed'} session setup failed for ${proxySecuritySession.targetType ?? 'unknown'} target ${proxySecuritySession.targetId ?? 'unknown'}: ${normalizeErrorMessage(error)}`,
|
|
2854
|
+
{ cause: error },
|
|
2855
|
+
);
|
|
2597
2856
|
}
|
|
2598
2857
|
})();
|
|
2599
2858
|
proxySecuritySession.setupPromise = setupPromise;
|
|
@@ -2619,6 +2878,18 @@ export class LiveBrowserSession {
|
|
|
2619
2878
|
if (!proxySecuritySession.targetId) {
|
|
2620
2879
|
throw new Error(`Proxy security session detached before target identification: ${session.id()}`);
|
|
2621
2880
|
}
|
|
2881
|
+
if (await this.waitForProxySecurityTargetCoverage(proxySecuritySession.targetId, generation)) {
|
|
2882
|
+
return;
|
|
2883
|
+
}
|
|
2884
|
+
throw new Error(
|
|
2885
|
+
`Proxy security detached from live target ${proxySecuritySession.targetId}`,
|
|
2886
|
+
);
|
|
2887
|
+
}
|
|
2888
|
+
|
|
2889
|
+
private async waitForProxySecurityTargetCoverage(
|
|
2890
|
+
targetId: string,
|
|
2891
|
+
generation: number,
|
|
2892
|
+
): Promise<boolean> {
|
|
2622
2893
|
for (let attempt = 0; attempt < 3; attempt += 1) {
|
|
2623
2894
|
const generationSessions = [...this.proxySecuritySessions.values()].filter((candidate) => (
|
|
2624
2895
|
candidate.generation === generation
|
|
@@ -2626,12 +2897,12 @@ export class LiveBrowserSession {
|
|
|
2626
2897
|
await Promise.allSettled(generationSessions.map((candidate) => candidate.setupPromise));
|
|
2627
2898
|
const hasReplacement = [...this.proxySecuritySessions.values()].some((candidate) => (
|
|
2628
2899
|
candidate.generation === generation
|
|
2629
|
-
&& candidate.targetId ===
|
|
2900
|
+
&& candidate.targetId === targetId
|
|
2630
2901
|
&& candidate.fetchEnabled
|
|
2631
2902
|
&& !candidate.session.detached
|
|
2632
2903
|
));
|
|
2633
2904
|
if (hasReplacement) {
|
|
2634
|
-
return;
|
|
2905
|
+
return true;
|
|
2635
2906
|
}
|
|
2636
2907
|
if (attempt < 2) {
|
|
2637
2908
|
await delay(25);
|
|
@@ -2641,12 +2912,12 @@ export class LiveBrowserSession {
|
|
|
2641
2912
|
if (!browserSecurityCdpSession || browserSecurityCdpSession.detached) {
|
|
2642
2913
|
throw new Error('Browser security CDP session detached unexpectedly');
|
|
2643
2914
|
}
|
|
2644
|
-
const { targetInfos } = await browserSecurityCdpSession.send(
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2915
|
+
const { targetInfos } = await browserSecurityCdpSession.send(
|
|
2916
|
+
'Target.getTargets',
|
|
2917
|
+
undefined,
|
|
2918
|
+
proxySecurityCommandOptions,
|
|
2919
|
+
);
|
|
2920
|
+
return !targetInfos.some((targetInfo) => targetInfo.targetId === targetId);
|
|
2650
2921
|
}
|
|
2651
2922
|
|
|
2652
2923
|
private removeProxySecuritySession(proxySecuritySession: IProxySecuritySession): void {
|
|
@@ -2668,6 +2939,113 @@ export class LiveBrowserSession {
|
|
|
2668
2939
|
this.proxySecuritySessions.delete(proxySecuritySession.session.id());
|
|
2669
2940
|
}
|
|
2670
2941
|
|
|
2942
|
+
private async detachOwnedProxySecuritySession(
|
|
2943
|
+
proxySecuritySession: IProxySecuritySession,
|
|
2944
|
+
): Promise<void> {
|
|
2945
|
+
if (!proxySecuritySession.ownedByProxySecurity) {
|
|
2946
|
+
return;
|
|
2947
|
+
}
|
|
2948
|
+
await this.detachTrackedOwnedProxySecuritySession(proxySecuritySession.session);
|
|
2949
|
+
}
|
|
2950
|
+
|
|
2951
|
+
private forgetOwnedProxySecuritySession(session: plugins.puppeteer.CDPSession): void {
|
|
2952
|
+
this.ownedProxySecuritySessions.delete(session);
|
|
2953
|
+
this.browserSecurityParentOwnedSessions.delete(session);
|
|
2954
|
+
}
|
|
2955
|
+
|
|
2956
|
+
private async detachCdpSessionWithTimeout(
|
|
2957
|
+
session: plugins.puppeteer.CDPSession,
|
|
2958
|
+
description: string,
|
|
2959
|
+
): Promise<void> {
|
|
2960
|
+
let timeout: ReturnType<typeof setTimeout> | undefined;
|
|
2961
|
+
const timeoutPromise = new Promise<never>((_resolve, reject) => {
|
|
2962
|
+
timeout = setTimeout(() => {
|
|
2963
|
+
reject(new Error(
|
|
2964
|
+
`${description} did not detach within ${proxySecurityCommandTimeoutMs}ms`,
|
|
2965
|
+
));
|
|
2966
|
+
}, proxySecurityCommandTimeoutMs);
|
|
2967
|
+
});
|
|
2968
|
+
try {
|
|
2969
|
+
await Promise.race([session.detach(), timeoutPromise]);
|
|
2970
|
+
} finally {
|
|
2971
|
+
if (timeout) {
|
|
2972
|
+
clearTimeout(timeout);
|
|
2973
|
+
}
|
|
2974
|
+
}
|
|
2975
|
+
}
|
|
2976
|
+
|
|
2977
|
+
private retireOwnedProxySecuritySession(
|
|
2978
|
+
proxySecuritySession: IProxySecuritySession,
|
|
2979
|
+
force: boolean,
|
|
2980
|
+
): void {
|
|
2981
|
+
if (
|
|
2982
|
+
!proxySecuritySession.ownedByProxySecurity
|
|
2983
|
+
|| this.proxySecuritySessions.get(proxySecuritySession.session.id())
|
|
2984
|
+
!== proxySecuritySession
|
|
2985
|
+
) {
|
|
2986
|
+
return;
|
|
2987
|
+
}
|
|
2988
|
+
proxySecuritySession.retireWhenIdle = true;
|
|
2989
|
+
if (
|
|
2990
|
+
!force
|
|
2991
|
+
&& (
|
|
2992
|
+
proxySecuritySession.attemptedAuthentications.size > 0
|
|
2993
|
+
|| proxySecuritySession.requestIdsByNetworkId.size > 0
|
|
2994
|
+
)
|
|
2995
|
+
) {
|
|
2996
|
+
return;
|
|
2997
|
+
}
|
|
2998
|
+
if (proxySecuritySession.targetId) {
|
|
2999
|
+
for (const [versionId, targetId] of this.serviceWorkerTargetIdsByVersionId) {
|
|
3000
|
+
if (targetId === proxySecuritySession.targetId) {
|
|
3001
|
+
this.serviceWorkerTargetIdsByVersionId.delete(versionId);
|
|
3002
|
+
}
|
|
3003
|
+
}
|
|
3004
|
+
}
|
|
3005
|
+
proxySecuritySession.allowLiveTargetDetach = true;
|
|
3006
|
+
this.removeProxySecuritySession(proxySecuritySession);
|
|
3007
|
+
this.trackProxySecurityOperation(
|
|
3008
|
+
this.detachOwnedProxySecuritySession(proxySecuritySession),
|
|
3009
|
+
proxySecuritySession.generation,
|
|
3010
|
+
'proxy_security_worker_retirement_failed',
|
|
3011
|
+
);
|
|
3012
|
+
}
|
|
3013
|
+
|
|
3014
|
+
private async detachTrackedOwnedProxySecuritySession(
|
|
3015
|
+
session: plugins.puppeteer.CDPSession,
|
|
3016
|
+
): Promise<void> {
|
|
3017
|
+
if (session.detached) {
|
|
3018
|
+
this.forgetOwnedProxySecuritySession(session);
|
|
3019
|
+
return;
|
|
3020
|
+
}
|
|
3021
|
+
try {
|
|
3022
|
+
if (this.browserSecurityParentOwnedSessions.has(session)) {
|
|
3023
|
+
const browserSecurityCdpSession = this.browserSecurityCdpSession;
|
|
3024
|
+
if (!browserSecurityCdpSession || browserSecurityCdpSession.detached) {
|
|
3025
|
+
throw new Error('Browser security parent session is unavailable');
|
|
3026
|
+
}
|
|
3027
|
+
await browserSecurityCdpSession.send('Target.detachFromTarget', {
|
|
3028
|
+
sessionId: session.id(),
|
|
3029
|
+
}, proxySecurityCommandOptions);
|
|
3030
|
+
} else {
|
|
3031
|
+
await this.detachCdpSessionWithTimeout(session, 'proxy security session');
|
|
3032
|
+
}
|
|
3033
|
+
this.forgetOwnedProxySecuritySession(session);
|
|
3034
|
+
} catch (error) {
|
|
3035
|
+
if (session.detached) {
|
|
3036
|
+
this.forgetOwnedProxySecuritySession(session);
|
|
3037
|
+
return;
|
|
3038
|
+
}
|
|
3039
|
+
if (this.proxySecurityStopping || this.normalStopRequested) {
|
|
3040
|
+
return;
|
|
3041
|
+
}
|
|
3042
|
+
throw new Error(
|
|
3043
|
+
`Failed to detach an owned proxy security session: ${normalizeErrorMessage(error)}`,
|
|
3044
|
+
{ cause: error },
|
|
3045
|
+
);
|
|
3046
|
+
}
|
|
3047
|
+
}
|
|
3048
|
+
|
|
2671
3049
|
private allowOperationalCdpSessionDetach(session: plugins.puppeteer.CDPSession): void {
|
|
2672
3050
|
const proxySecuritySession = this.proxySecuritySessions.get(session.id());
|
|
2673
3051
|
if (proxySecuritySession) {
|
|
@@ -2730,12 +3108,51 @@ export class LiveBrowserSession {
|
|
|
2730
3108
|
|
|
2731
3109
|
private async teardownProxySecurity(): Promise<void> {
|
|
2732
3110
|
this.proxySecurityStopping = true;
|
|
3111
|
+
this.serviceWorkerTargetIdsByVersionId.clear();
|
|
3112
|
+
const browserSecurityCdpSession = this.browserSecurityCdpSession;
|
|
3113
|
+
if (browserSecurityCdpSession && !browserSecurityCdpSession.detached) {
|
|
3114
|
+
try {
|
|
3115
|
+
await browserSecurityCdpSession.send('Target.setAutoAttach', {
|
|
3116
|
+
autoAttach: false,
|
|
3117
|
+
waitForDebuggerOnStart: false,
|
|
3118
|
+
flatten: true,
|
|
3119
|
+
}, proxySecurityCommandOptions);
|
|
3120
|
+
} catch {
|
|
3121
|
+
// Browser shutdown may detach the browser target before auto-attach is disabled.
|
|
3122
|
+
}
|
|
3123
|
+
}
|
|
3124
|
+
const workerLifecycleCdpSession = this.browserSecurityWorkerLifecycleCdpSession;
|
|
3125
|
+
if (workerLifecycleCdpSession && !workerLifecycleCdpSession.detached) {
|
|
3126
|
+
try {
|
|
3127
|
+
await workerLifecycleCdpSession.send(
|
|
3128
|
+
'ServiceWorker.disable',
|
|
3129
|
+
undefined,
|
|
3130
|
+
proxySecurityCommandOptions,
|
|
3131
|
+
);
|
|
3132
|
+
} catch {
|
|
3133
|
+
// Browser shutdown can close the hidden lifecycle target first.
|
|
3134
|
+
}
|
|
3135
|
+
}
|
|
3136
|
+
if (workerLifecycleCdpSession && this.browserSecurityWorkerVersionUpdatedListener) {
|
|
3137
|
+
workerLifecycleCdpSession.off(
|
|
3138
|
+
'ServiceWorker.workerVersionUpdated',
|
|
3139
|
+
this.browserSecurityWorkerVersionUpdatedListener,
|
|
3140
|
+
);
|
|
3141
|
+
}
|
|
3142
|
+
this.browserSecurityWorkerVersionUpdatedListener = undefined;
|
|
3143
|
+
this.browserSecurityWorkerLifecycleCdpSession = undefined;
|
|
2733
3144
|
if (this.browserSecurityConnection && this.browserSecuritySessionAttachedListener) {
|
|
2734
3145
|
this.browserSecurityConnection.off(
|
|
2735
3146
|
'sessionattached',
|
|
2736
3147
|
this.browserSecuritySessionAttachedListener,
|
|
2737
3148
|
);
|
|
2738
3149
|
}
|
|
3150
|
+
if (browserSecurityCdpSession && this.browserSecurityOwnedSessionAttachedListener) {
|
|
3151
|
+
browserSecurityCdpSession.off(
|
|
3152
|
+
plugins.puppeteer.CDPSessionEvent.SessionAttached,
|
|
3153
|
+
this.browserSecurityOwnedSessionAttachedListener,
|
|
3154
|
+
);
|
|
3155
|
+
}
|
|
2739
3156
|
if (this.browserSecurityConnection && this.browserSecuritySessionDetachedListener) {
|
|
2740
3157
|
this.browserSecurityConnection.off(
|
|
2741
3158
|
'sessiondetached',
|
|
@@ -2743,28 +3160,31 @@ export class LiveBrowserSession {
|
|
|
2743
3160
|
);
|
|
2744
3161
|
}
|
|
2745
3162
|
this.browserSecuritySessionAttachedListener = undefined;
|
|
3163
|
+
this.browserSecurityOwnedSessionAttachedListener = undefined;
|
|
2746
3164
|
this.browserSecuritySessionDetachedListener = undefined;
|
|
2747
3165
|
this.browserSecurityConnection = undefined;
|
|
2748
|
-
const ownedSessions = [...this.
|
|
2749
|
-
.filter((record) => record.ownedByProxySecurity)
|
|
2750
|
-
.map((record) => record.session);
|
|
3166
|
+
const ownedSessions = [...this.ownedProxySecuritySessions];
|
|
2751
3167
|
for (const proxySecuritySession of [...this.proxySecuritySessions.values()]) {
|
|
2752
3168
|
this.removeProxySecuritySession(proxySecuritySession);
|
|
2753
3169
|
}
|
|
2754
3170
|
for (const ownedSession of ownedSessions) {
|
|
2755
3171
|
if (!ownedSession.detached) {
|
|
2756
3172
|
try {
|
|
2757
|
-
await
|
|
3173
|
+
await this.detachTrackedOwnedProxySecuritySession(ownedSession);
|
|
2758
3174
|
} catch {
|
|
2759
3175
|
// Browser shutdown can close a target before explicit detach settles.
|
|
2760
3176
|
}
|
|
2761
3177
|
}
|
|
2762
3178
|
}
|
|
2763
|
-
|
|
3179
|
+
this.ownedProxySecuritySessions.clear();
|
|
3180
|
+
this.browserSecurityParentOwnedSessions.clear();
|
|
2764
3181
|
this.browserSecurityCdpSession = undefined;
|
|
2765
3182
|
if (browserSecurityCdpSession && !browserSecurityCdpSession.detached) {
|
|
2766
3183
|
try {
|
|
2767
|
-
await
|
|
3184
|
+
await this.detachCdpSessionWithTimeout(
|
|
3185
|
+
browserSecurityCdpSession,
|
|
3186
|
+
'browser security session',
|
|
3187
|
+
);
|
|
2768
3188
|
} catch {
|
|
2769
3189
|
// Browser shutdown can detach the browser target first.
|
|
2770
3190
|
}
|
|
@@ -2927,7 +3347,8 @@ export class LiveBrowserSession {
|
|
|
2927
3347
|
closing: false,
|
|
2928
3348
|
stateUpdateQueued: false,
|
|
2929
3349
|
stateUpdatePending: false,
|
|
2930
|
-
|
|
3350
|
+
navigationResetRevision: 0,
|
|
3351
|
+
restoredNavigationRevision: 0,
|
|
2931
3352
|
removeListeners: [],
|
|
2932
3353
|
};
|
|
2933
3354
|
this.tabs.set(tab.id, tab);
|
|
@@ -2939,11 +3360,11 @@ export class LiveBrowserSession {
|
|
|
2939
3360
|
tab.securityCdpSession = securityCdpSession;
|
|
2940
3361
|
await securityCdpSession.send('Page.enable', {
|
|
2941
3362
|
enableFileChooserOpenedEvent: true,
|
|
2942
|
-
});
|
|
3363
|
+
}, proxySecurityCommandOptions);
|
|
2943
3364
|
await securityCdpSession.send('Page.setInterceptFileChooserDialog', {
|
|
2944
3365
|
enabled: true,
|
|
2945
3366
|
cancel: true,
|
|
2946
|
-
});
|
|
3367
|
+
}, proxySecurityCommandOptions);
|
|
2947
3368
|
}
|
|
2948
3369
|
await this.ensureTabViewport(tab);
|
|
2949
3370
|
await this.refreshTab(tab);
|
|
@@ -3078,7 +3499,10 @@ export class LiveBrowserSession {
|
|
|
3078
3499
|
} catch (error) {
|
|
3079
3500
|
if (tab.securityCdpSession && !tab.securityCdpSession.detached) {
|
|
3080
3501
|
try {
|
|
3081
|
-
await
|
|
3502
|
+
await this.detachCdpSessionWithTimeout(
|
|
3503
|
+
tab.securityCdpSession,
|
|
3504
|
+
'page security session',
|
|
3505
|
+
);
|
|
3082
3506
|
} catch {
|
|
3083
3507
|
// The page may have closed while security setup was failing.
|
|
3084
3508
|
}
|
|
@@ -3117,31 +3541,58 @@ export class LiveBrowserSession {
|
|
|
3117
3541
|
return;
|
|
3118
3542
|
}
|
|
3119
3543
|
tab.stateUpdatePending = true;
|
|
3120
|
-
|
|
3544
|
+
if (navigationReset) {
|
|
3545
|
+
tab.navigationResetRevision += 1;
|
|
3546
|
+
}
|
|
3121
3547
|
if (tab.stateUpdateQueued) {
|
|
3122
3548
|
return;
|
|
3123
3549
|
}
|
|
3124
3550
|
tab.stateUpdateQueued = true;
|
|
3125
3551
|
const wasScheduled = this.scheduleOperation(async () => {
|
|
3126
3552
|
try {
|
|
3127
|
-
const
|
|
3553
|
+
const navigationResetRevision = tab.navigationResetRevision;
|
|
3554
|
+
const shouldResetNavigation = (
|
|
3555
|
+
tab.restoredNavigationRevision < navigationResetRevision
|
|
3556
|
+
);
|
|
3128
3557
|
tab.stateUpdatePending = false;
|
|
3129
|
-
tab.navigationResetPending = false;
|
|
3130
3558
|
if (!this.tabs.has(tab.id) || tab.closing) {
|
|
3131
3559
|
return;
|
|
3132
3560
|
}
|
|
3133
3561
|
if (shouldResetNavigation && this.activeTabId === tab.id) {
|
|
3134
3562
|
await this.stopScreencast(tab);
|
|
3135
3563
|
}
|
|
3136
|
-
|
|
3137
|
-
|
|
3564
|
+
let stateUpdateError: unknown;
|
|
3565
|
+
try {
|
|
3566
|
+
await this.refreshTab(tab);
|
|
3567
|
+
this.emitState();
|
|
3568
|
+
} catch (error) {
|
|
3569
|
+
stateUpdateError = error;
|
|
3570
|
+
}
|
|
3138
3571
|
if (
|
|
3139
3572
|
shouldResetNavigation
|
|
3140
3573
|
&& this.activeTabId === tab.id
|
|
3141
3574
|
&& this.status === 'running'
|
|
3142
3575
|
&& tab.status === 'open'
|
|
3143
3576
|
) {
|
|
3144
|
-
|
|
3577
|
+
try {
|
|
3578
|
+
await this.startScreencast(tab);
|
|
3579
|
+
} catch (error) {
|
|
3580
|
+
throw stateUpdateError
|
|
3581
|
+
? new AggregateError(
|
|
3582
|
+
[stateUpdateError, error],
|
|
3583
|
+
'Page state refresh and screencast restart failed',
|
|
3584
|
+
)
|
|
3585
|
+
: error;
|
|
3586
|
+
}
|
|
3587
|
+
}
|
|
3588
|
+
if (shouldResetNavigation) {
|
|
3589
|
+
tab.restoredNavigationRevision = Math.max(
|
|
3590
|
+
tab.restoredNavigationRevision,
|
|
3591
|
+
navigationResetRevision,
|
|
3592
|
+
);
|
|
3593
|
+
}
|
|
3594
|
+
if (stateUpdateError) {
|
|
3595
|
+
throw stateUpdateError;
|
|
3145
3596
|
}
|
|
3146
3597
|
} finally {
|
|
3147
3598
|
tab.stateUpdateQueued = false;
|
|
@@ -3346,15 +3797,32 @@ export class LiveBrowserSession {
|
|
|
3346
3797
|
} finally {
|
|
3347
3798
|
tab.navigationInProgress = false;
|
|
3348
3799
|
if (this.tabs.has(tab.id) && !tab.page.isClosed()) {
|
|
3349
|
-
|
|
3350
|
-
|
|
3800
|
+
let stateUpdateError: unknown;
|
|
3801
|
+
try {
|
|
3802
|
+
await this.refreshTab(tab);
|
|
3803
|
+
this.emitState();
|
|
3804
|
+
} catch (error) {
|
|
3805
|
+
stateUpdateError = error;
|
|
3806
|
+
}
|
|
3351
3807
|
if (
|
|
3352
3808
|
isActive
|
|
3353
3809
|
&& this.activeTabId === tab.id
|
|
3354
3810
|
&& this.status === 'running'
|
|
3355
3811
|
&& tab.status === 'open'
|
|
3356
3812
|
) {
|
|
3357
|
-
|
|
3813
|
+
try {
|
|
3814
|
+
await this.startScreencast(tab);
|
|
3815
|
+
} catch (error) {
|
|
3816
|
+
throw stateUpdateError
|
|
3817
|
+
? new AggregateError(
|
|
3818
|
+
[stateUpdateError, error],
|
|
3819
|
+
'Navigation state refresh and screencast restart failed',
|
|
3820
|
+
)
|
|
3821
|
+
: error;
|
|
3822
|
+
}
|
|
3823
|
+
}
|
|
3824
|
+
if (stateUpdateError) {
|
|
3825
|
+
throw stateUpdateError;
|
|
3358
3826
|
}
|
|
3359
3827
|
}
|
|
3360
3828
|
}
|