craftdriver 1.7.0 → 1.8.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/CHANGELOG.md +23 -11
- package/README.md +13 -4
- package/dist/cli/agentSession.d.ts +62 -0
- package/dist/cli/agentSession.d.ts.map +1 -0
- package/dist/cli/agentSession.js +100 -0
- package/dist/cli/agentSession.js.map +1 -0
- package/dist/cli/artifactPaths.d.ts +42 -0
- package/dist/cli/artifactPaths.d.ts.map +1 -0
- package/dist/cli/artifactPaths.js +144 -0
- package/dist/cli/artifactPaths.js.map +1 -0
- package/dist/cli/bounds.d.ts +88 -0
- package/dist/cli/bounds.d.ts.map +1 -0
- package/dist/cli/bounds.js +128 -0
- package/dist/cli/bounds.js.map +1 -0
- package/dist/cli/client.d.ts +3 -0
- package/dist/cli/client.d.ts.map +1 -1
- package/dist/cli/client.js +24 -8
- package/dist/cli/client.js.map +1 -1
- package/dist/cli/daemon.d.ts +3 -0
- package/dist/cli/daemon.d.ts.map +1 -1
- package/dist/cli/daemon.js +152 -39
- package/dist/cli/daemon.js.map +1 -1
- package/dist/cli/defaults.d.ts +23 -3
- package/dist/cli/defaults.d.ts.map +1 -1
- package/dist/cli/defaults.js +50 -5
- package/dist/cli/defaults.js.map +1 -1
- package/dist/cli/dispatcher.d.ts +54 -11
- package/dist/cli/dispatcher.d.ts.map +1 -1
- package/dist/cli/dispatcher.js +851 -54
- package/dist/cli/dispatcher.js.map +1 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +305 -56
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/init.d.ts +14 -14
- package/dist/cli/init.d.ts.map +1 -1
- package/dist/cli/init.js +328 -180
- package/dist/cli/init.js.map +1 -1
- package/dist/cli/journal.d.ts +153 -0
- package/dist/cli/journal.d.ts.map +1 -0
- package/dist/cli/journal.js +252 -0
- package/dist/cli/journal.js.map +1 -0
- package/dist/cli/lineReader.d.ts +33 -0
- package/dist/cli/lineReader.d.ts.map +1 -0
- package/dist/cli/lineReader.js +66 -0
- package/dist/cli/lineReader.js.map +1 -0
- package/dist/cli/locatorCandidates.d.ts +44 -0
- package/dist/cli/locatorCandidates.d.ts.map +1 -0
- package/dist/cli/locatorCandidates.js +190 -0
- package/dist/cli/locatorCandidates.js.map +1 -0
- package/dist/cli/mcp/artifacts.d.ts +39 -1
- package/dist/cli/mcp/artifacts.d.ts.map +1 -1
- package/dist/cli/mcp/artifacts.js +104 -9
- package/dist/cli/mcp/artifacts.js.map +1 -1
- package/dist/cli/mcp/bounds.d.ts +65 -0
- package/dist/cli/mcp/bounds.d.ts.map +1 -0
- package/dist/cli/mcp/bounds.js +124 -0
- package/dist/cli/mcp/bounds.js.map +1 -0
- package/dist/cli/mcp/params.d.ts +38 -0
- package/dist/cli/mcp/params.d.ts.map +1 -0
- package/dist/cli/mcp/params.js +174 -0
- package/dist/cli/mcp/params.js.map +1 -0
- package/dist/cli/mcp/server.d.ts +94 -16
- package/dist/cli/mcp/server.d.ts.map +1 -1
- package/dist/cli/mcp/server.js +304 -150
- package/dist/cli/mcp/server.js.map +1 -1
- package/dist/cli/mcp/tools.d.ts +62 -17
- package/dist/cli/mcp/tools.d.ts.map +1 -1
- package/dist/cli/mcp/tools.js +373 -158
- package/dist/cli/mcp/tools.js.map +1 -1
- package/dist/cli/pageSemantics.d.ts +42 -0
- package/dist/cli/pageSemantics.d.ts.map +1 -0
- package/dist/cli/pageSemantics.js +194 -0
- package/dist/cli/pageSemantics.js.map +1 -0
- package/dist/cli/parseArgs.d.ts +6 -18
- package/dist/cli/parseArgs.d.ts.map +1 -1
- package/dist/cli/parseArgs.js +689 -45
- package/dist/cli/parseArgs.js.map +1 -1
- package/dist/cli/protocol.d.ts +8 -0
- package/dist/cli/protocol.d.ts.map +1 -1
- package/dist/cli/sessionRegistry.d.ts +42 -0
- package/dist/cli/sessionRegistry.d.ts.map +1 -0
- package/dist/cli/sessionRegistry.js +99 -0
- package/dist/cli/sessionRegistry.js.map +1 -0
- package/dist/cli/snapshot.d.ts +100 -18
- package/dist/cli/snapshot.d.ts.map +1 -1
- package/dist/cli/snapshot.js +248 -102
- package/dist/cli/snapshot.js.map +1 -1
- package/dist/cli/stateStore.d.ts +50 -0
- package/dist/cli/stateStore.d.ts.map +1 -0
- package/dist/cli/stateStore.js +122 -0
- package/dist/cli/stateStore.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/lib/bidi/index.d.ts +1 -1
- package/dist/lib/bidi/index.d.ts.map +1 -1
- package/dist/lib/bidi/index.js +2 -2
- package/dist/lib/bidi/index.js.map +1 -1
- package/dist/lib/bidi/logs.d.ts +3 -0
- package/dist/lib/bidi/logs.d.ts.map +1 -1
- package/dist/lib/bidi/logs.js +7 -0
- package/dist/lib/bidi/logs.js.map +1 -1
- package/dist/lib/bidi/network.d.ts +14 -1
- package/dist/lib/bidi/network.d.ts.map +1 -1
- package/dist/lib/bidi/network.js +45 -7
- package/dist/lib/bidi/network.js.map +1 -1
- package/dist/lib/bidi/storage.d.ts +30 -6
- package/dist/lib/bidi/storage.d.ts.map +1 -1
- package/dist/lib/bidi/storage.js +190 -45
- package/dist/lib/bidi/storage.js.map +1 -1
- package/dist/lib/bidi/types.d.ts +7 -2
- package/dist/lib/bidi/types.d.ts.map +1 -1
- package/dist/lib/browser.d.ts +6 -3
- package/dist/lib/browser.d.ts.map +1 -1
- package/dist/lib/browser.js +140 -30
- package/dist/lib/browser.js.map +1 -1
- package/dist/lib/browserContext.d.ts +54 -18
- package/dist/lib/browserContext.d.ts.map +1 -1
- package/dist/lib/browserContext.js +295 -92
- package/dist/lib/browserContext.js.map +1 -1
- package/dist/lib/by.d.ts +3 -3
- package/dist/lib/by.d.ts.map +1 -1
- package/dist/lib/by.js +7 -3
- package/dist/lib/by.js.map +1 -1
- package/dist/lib/driver.d.ts +7 -0
- package/dist/lib/driver.d.ts.map +1 -1
- package/dist/lib/driver.js +12 -0
- package/dist/lib/driver.js.map +1 -1
- package/dist/lib/errors.d.ts +8 -0
- package/dist/lib/errors.d.ts.map +1 -1
- package/dist/lib/errors.js +8 -0
- package/dist/lib/errors.js.map +1 -1
- package/dist/lib/initScript.d.ts +9 -0
- package/dist/lib/initScript.d.ts.map +1 -0
- package/dist/lib/initScript.js +14 -0
- package/dist/lib/initScript.js.map +1 -0
- package/dist/lib/page.d.ts +22 -0
- package/dist/lib/page.d.ts.map +1 -1
- package/dist/lib/page.js +48 -0
- package/dist/lib/page.js.map +1 -1
- package/dist/lib/secureFile.d.ts +8 -0
- package/dist/lib/secureFile.d.ts.map +1 -0
- package/dist/lib/secureFile.js +67 -0
- package/dist/lib/secureFile.js.map +1 -0
- package/dist/lib/sessionStateValidation.d.ts +35 -0
- package/dist/lib/sessionStateValidation.d.ts.map +1 -0
- package/dist/lib/sessionStateValidation.js +223 -0
- package/dist/lib/sessionStateValidation.js.map +1 -0
- package/dist/lib/tracing.d.ts +2 -1
- package/dist/lib/tracing.d.ts.map +1 -1
- package/dist/lib/tracing.js +12 -1
- package/dist/lib/tracing.js.map +1 -1
- package/docs/agents.md +56 -61
- package/docs/api-reference.md +2 -1
- package/docs/browser-api.md +24 -4
- package/docs/browser-context.md +18 -12
- package/docs/cli.md +346 -26
- package/docs/error-codes.md +5 -4
- package/docs/getting-started.md +6 -1
- package/docs/index.md +1 -1
- package/docs/mcp.md +129 -62
- package/docs/public/examples/agent-actions.html +48 -0
- package/docs/public/examples/agent-debug.html +86 -0
- package/docs/public/examples/agent-late-mutation.html +18 -0
- package/docs/public/examples/agent-ref-shift.html +33 -0
- package/docs/recipes/login-once-reuse-session.md +32 -4
- package/docs/session-management.md +64 -3
- package/docs/why-craftdriver.md +1 -1
- package/package.json +3 -1
- package/skills/craftdriver/SKILL.md +59 -75
- package/skills/craftdriver/agents/openai.yaml +4 -0
- package/skills/craftdriver/cheatsheet.md +18 -4
- package/skills/craftdriver/cli.md +194 -68
- package/skills/craftdriver/patterns.md +14 -8
- package/skills/craftdriver/workflow.md +149 -0
package/dist/lib/browser.js
CHANGED
|
@@ -33,10 +33,12 @@ import { Tracer } from './tracing.js';
|
|
|
33
33
|
import { A11y } from './a11y.js';
|
|
34
34
|
import { Clock } from './clock.js';
|
|
35
35
|
import { CraftdriverError, ErrorCode } from './errors.js';
|
|
36
|
+
import { parseSessionState, hasNonEmptySessionStorage, isSessionStateEmpty, storageStateDetail, } from './sessionStateValidation.js';
|
|
36
37
|
import { clickWithFastPath } from './clickFastPath.js';
|
|
37
38
|
import { fillWithFastPath } from './fillFastPath.js';
|
|
38
39
|
import { clearWithFastPath } from './clearFastPath.js';
|
|
39
40
|
import { runExpectScreenshot, shouldUpdateVisualBaselines } from './visual/index.js';
|
|
41
|
+
import { publicPageInitScript } from './initScript.js';
|
|
40
42
|
/** Common mobile device presets */
|
|
41
43
|
export const devices = {
|
|
42
44
|
'iPhone 14': {
|
|
@@ -269,6 +271,7 @@ export class Browser {
|
|
|
269
271
|
_topLevelContextTracking;
|
|
270
272
|
_topLevelContextTrackingOffs = [];
|
|
271
273
|
_topLevelContextCacheVersion = 0;
|
|
274
|
+
_destroyedTopLevelContextIds = new Set();
|
|
272
275
|
_logs;
|
|
273
276
|
_tracer;
|
|
274
277
|
_storage;
|
|
@@ -481,9 +484,19 @@ export class Browser {
|
|
|
481
484
|
if (wsUrl && bidiRequested) {
|
|
482
485
|
await browser.initBiDi(wsUrl);
|
|
483
486
|
}
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
+
if (options.storageState !== undefined) {
|
|
488
|
+
const protocol = browser.bidiSession?.isConnected() ? 'bidi' : 'classic';
|
|
489
|
+
const errorContext = { operation: 'Browser.launch', browserName: name, protocol };
|
|
490
|
+
const state = await parseSessionState(options.storageState, errorContext);
|
|
491
|
+
if (browser.bidiSession?.isConnected()) {
|
|
492
|
+
await browser.defaultContext._loadStorageState(state, 'Browser.launch');
|
|
493
|
+
}
|
|
494
|
+
else if (!isSessionStateEmpty(state)) {
|
|
495
|
+
throw new CraftdriverError(ErrorCode.UNSUPPORTED, 'non-empty storageState cannot be restored at WebDriver Classic launch', {
|
|
496
|
+
detail: storageStateDetail(errorContext, 'capability', { partialApplied: false }),
|
|
497
|
+
hint: 'Enable BiDi, or launch first, navigate to the sole origin, then call browser.loadState().',
|
|
498
|
+
});
|
|
499
|
+
}
|
|
487
500
|
}
|
|
488
501
|
}
|
|
489
502
|
catch (err) {
|
|
@@ -517,8 +530,23 @@ export class Browser {
|
|
|
517
530
|
if (wsUrl && target.bidiRequested) {
|
|
518
531
|
await browser.initBiDi(wsUrl);
|
|
519
532
|
}
|
|
520
|
-
if (options.storageState) {
|
|
521
|
-
|
|
533
|
+
if (options.storageState !== undefined) {
|
|
534
|
+
const protocol = browser.bidiSession?.isConnected() ? 'bidi' : 'classic';
|
|
535
|
+
const errorContext = {
|
|
536
|
+
operation: 'Browser.launch',
|
|
537
|
+
browserName: target.browserName,
|
|
538
|
+
protocol,
|
|
539
|
+
};
|
|
540
|
+
const state = await parseSessionState(options.storageState, errorContext);
|
|
541
|
+
if (browser.bidiSession?.isConnected()) {
|
|
542
|
+
await browser.defaultContext._loadStorageState(state, 'Browser.launch');
|
|
543
|
+
}
|
|
544
|
+
else if (!isSessionStateEmpty(state)) {
|
|
545
|
+
throw new CraftdriverError(ErrorCode.UNSUPPORTED, 'non-empty storageState cannot be restored at WebDriver Classic launch', {
|
|
546
|
+
detail: storageStateDetail(errorContext, 'capability', { partialApplied: false }),
|
|
547
|
+
hint: 'Enable BiDi, or launch first, navigate to the sole origin, then call browser.loadState().',
|
|
548
|
+
});
|
|
549
|
+
}
|
|
522
550
|
}
|
|
523
551
|
}
|
|
524
552
|
catch (err) {
|
|
@@ -550,6 +578,8 @@ export class Browser {
|
|
|
550
578
|
this._topLevelContextTracking = this._startTopLevelContextTracking(contexts);
|
|
551
579
|
},
|
|
552
580
|
});
|
|
581
|
+
session.network.setInternalContextPredicate((context) => this._isInternalContextId(context));
|
|
582
|
+
session.logs.setInternalContextPredicate((context) => this._isInternalContextId(context));
|
|
553
583
|
await this._ensureTopLevelContextTracking().catch(() => { });
|
|
554
584
|
return; // success
|
|
555
585
|
}
|
|
@@ -618,7 +648,13 @@ export class Browser {
|
|
|
618
648
|
*/
|
|
619
649
|
get storage() {
|
|
620
650
|
if (!this._storage) {
|
|
621
|
-
this._storage = new SessionStateManager(this.driver, this.bidiSession?.isConnected() ? this.bidiSession.getConnection() : null
|
|
651
|
+
this._storage = new SessionStateManager(this.driver, this.bidiSession?.isConnected() ? this.bidiSession.getConnection() : null, undefined, {
|
|
652
|
+
browserName: this._browserName,
|
|
653
|
+
protocol: this.bidiSession?.isConnected() ? 'bidi' : 'classic',
|
|
654
|
+
runSerializedRestore: this.bidiSession?.isConnected()
|
|
655
|
+
? (task) => this.defaultContext._runSerializedRestore(task)
|
|
656
|
+
: undefined,
|
|
657
|
+
});
|
|
622
658
|
}
|
|
623
659
|
return this._storage;
|
|
624
660
|
}
|
|
@@ -681,8 +717,23 @@ export class Browser {
|
|
|
681
717
|
/**
|
|
682
718
|
* Load session state from file
|
|
683
719
|
*/
|
|
684
|
-
async loadState(
|
|
685
|
-
|
|
720
|
+
async loadState(source) {
|
|
721
|
+
const protocol = this.bidiSession?.isConnected() ? 'bidi' : 'classic';
|
|
722
|
+
const state = await parseSessionState(source, {
|
|
723
|
+
operation: 'browser.loadState',
|
|
724
|
+
browserName: this._browserName,
|
|
725
|
+
protocol,
|
|
726
|
+
});
|
|
727
|
+
// BiDi with no sessionStorage → the validated hydrator (multi-origin, no
|
|
728
|
+
// active page required). Otherwise (Classic, or sessionStorage present) take
|
|
729
|
+
// the active-page path, which restores a single origin matching the active
|
|
730
|
+
// page and can carry sessionStorage.
|
|
731
|
+
if (this.bidiSession?.isConnected() && !hasNonEmptySessionStorage(state)) {
|
|
732
|
+
await this.defaultContext._loadStorageState(state, 'browser.loadState');
|
|
733
|
+
}
|
|
734
|
+
else {
|
|
735
|
+
await this.storage.setState(state, 'browser.loadState');
|
|
736
|
+
}
|
|
686
737
|
}
|
|
687
738
|
async _runTracedAction(name, args, selector, run) {
|
|
688
739
|
const actionIndex = this._tracer?.recordAction(name, args, selector);
|
|
@@ -1135,6 +1186,7 @@ export class Browser {
|
|
|
1135
1186
|
this._topLevelContextTrackingOffs = [];
|
|
1136
1187
|
this._topLevelContextTracking = undefined;
|
|
1137
1188
|
this._topLevelContextUserContexts.clear();
|
|
1189
|
+
this._destroyedTopLevelContextIds.clear();
|
|
1138
1190
|
this._topLevelContextCacheVersion++;
|
|
1139
1191
|
// DELETE the WebDriver session — this tells the driver service to close the browser.
|
|
1140
1192
|
await this.driver.quit().catch(() => { });
|
|
@@ -1179,7 +1231,7 @@ export class Browser {
|
|
|
1179
1231
|
'Tracing relies on BiDi events; Classic WebDriver does not expose them.', { detail: { browserName: this._browserName, feature: 'startTrace' } });
|
|
1180
1232
|
}
|
|
1181
1233
|
if (!this._tracer) {
|
|
1182
|
-
this._tracer = new Tracer(this, this.bidiSession.getConnection(), this._engine);
|
|
1234
|
+
this._tracer = new Tracer(this, this.bidiSession.getConnection(), this._engine, (context) => this._isInternalContextId(context));
|
|
1183
1235
|
}
|
|
1184
1236
|
await this._tracer.start(opts);
|
|
1185
1237
|
}
|
|
@@ -1278,7 +1330,7 @@ export class Browser {
|
|
|
1278
1330
|
const conn = this.bidiSession.getConnection();
|
|
1279
1331
|
const fnSrc = typeof fnOrSrc === 'function' ? fnOrSrc.toString() : `() => { ${fnOrSrc} }`;
|
|
1280
1332
|
const result = await conn.send('script.addPreloadScript', {
|
|
1281
|
-
functionDeclaration: fnSrc,
|
|
1333
|
+
functionDeclaration: publicPageInitScript(fnSrc),
|
|
1282
1334
|
});
|
|
1283
1335
|
const scriptId = result.script;
|
|
1284
1336
|
this._browserInitScriptIds.add(scriptId);
|
|
@@ -1611,7 +1663,14 @@ export class Browser {
|
|
|
1611
1663
|
const tree = await conn.send('browsingContext.getTree', {
|
|
1612
1664
|
maxDepth: 0,
|
|
1613
1665
|
});
|
|
1614
|
-
|
|
1666
|
+
const pages = [];
|
|
1667
|
+
for (const ctx of tree.contexts ?? []) {
|
|
1668
|
+
const owner = this._wrapContext(ctx.userContext ?? 'default');
|
|
1669
|
+
if (await owner._isInternalPageContext(ctx.context))
|
|
1670
|
+
continue;
|
|
1671
|
+
pages.push(new Page(this.driver, ctx.context, this.getDefaultTimeout, conn, owner));
|
|
1672
|
+
}
|
|
1673
|
+
return pages;
|
|
1615
1674
|
}
|
|
1616
1675
|
// Classic fallback: use window handles
|
|
1617
1676
|
const handles = await this.driver.getWindowHandles();
|
|
@@ -1657,19 +1716,38 @@ export class Browser {
|
|
|
1657
1716
|
// `BiDiSession.connect()` (which must have run for `isConnected()` to be
|
|
1658
1717
|
// true), so no per-call `subscribe()` round trip is needed here.
|
|
1659
1718
|
return new Promise((resolve, reject) => {
|
|
1719
|
+
let settled = false;
|
|
1660
1720
|
const timer = setTimeout(() => {
|
|
1721
|
+
settled = true;
|
|
1661
1722
|
off();
|
|
1662
1723
|
reject(new Error(`waitForPage() timed out after ${timeout}ms`));
|
|
1663
1724
|
}, timeout);
|
|
1664
1725
|
const off = conn.on('browsingContext.contextCreated', (params) => {
|
|
1665
1726
|
// Only top-level contexts have no parent
|
|
1666
1727
|
if (!params.parent) {
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1728
|
+
const id = params.context;
|
|
1729
|
+
const owner = this._wrapContext(params.userContext ?? 'default');
|
|
1730
|
+
void owner._isInternalPageContext(id).then((internal) => {
|
|
1731
|
+
if (internal || settled)
|
|
1732
|
+
return;
|
|
1733
|
+
settled = true;
|
|
1734
|
+
clearTimeout(timer);
|
|
1735
|
+
off();
|
|
1736
|
+
resolve(new Page(this.driver, id, this.getDefaultTimeout, conn, owner));
|
|
1737
|
+
}).catch((err) => {
|
|
1738
|
+
if (settled)
|
|
1739
|
+
return;
|
|
1740
|
+
settled = true;
|
|
1741
|
+
clearTimeout(timer);
|
|
1742
|
+
off();
|
|
1743
|
+
reject(err);
|
|
1744
|
+
});
|
|
1670
1745
|
}
|
|
1671
1746
|
});
|
|
1672
1747
|
action().catch((err) => {
|
|
1748
|
+
if (settled)
|
|
1749
|
+
return;
|
|
1750
|
+
settled = true;
|
|
1673
1751
|
clearTimeout(timer);
|
|
1674
1752
|
off();
|
|
1675
1753
|
reject(err);
|
|
@@ -1797,6 +1875,15 @@ export class Browser {
|
|
|
1797
1875
|
});
|
|
1798
1876
|
return ctx;
|
|
1799
1877
|
}
|
|
1878
|
+
_isInternalContextId(context) {
|
|
1879
|
+
if (typeof context !== 'string')
|
|
1880
|
+
return false;
|
|
1881
|
+
for (const ctx of this._contextsById.values()) {
|
|
1882
|
+
if (ctx._isInternalPageId(context))
|
|
1883
|
+
return true;
|
|
1884
|
+
}
|
|
1885
|
+
return false;
|
|
1886
|
+
}
|
|
1800
1887
|
async newContext(opts) {
|
|
1801
1888
|
this.requireBiDi('newContext()', 'newContext() requires BiDi (enableBiDi: true). ' +
|
|
1802
1889
|
'WebDriver Classic has no concept of user contexts.');
|
|
@@ -1806,17 +1893,25 @@ export class Browser {
|
|
|
1806
1893
|
baseURL: opts?.baseURL,
|
|
1807
1894
|
extraHTTPHeaders: opts?.extraHTTPHeaders,
|
|
1808
1895
|
});
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1896
|
+
try {
|
|
1897
|
+
if (opts?.storageState !== undefined) {
|
|
1898
|
+
await ctx._loadStorageState(opts.storageState, 'browser.newContext');
|
|
1899
|
+
}
|
|
1900
|
+
// Apply identity & device emulation options. Each setter is `userContexts`-scoped,
|
|
1901
|
+
// so future pages in this context inherit automatically.
|
|
1902
|
+
if (opts?.locale !== undefined)
|
|
1903
|
+
await ctx.setLocale(opts.locale);
|
|
1904
|
+
if (opts?.timezoneId !== undefined)
|
|
1905
|
+
await ctx.setTimezone(opts.timezoneId);
|
|
1906
|
+
if (opts?.geolocation !== undefined)
|
|
1907
|
+
await ctx.setGeolocation(opts.geolocation);
|
|
1908
|
+
}
|
|
1909
|
+
catch (err) {
|
|
1910
|
+
// Restore/emulation failed — tear the just-created context down so a
|
|
1911
|
+
// rejected newContext() never leaves an orphaned user context behind.
|
|
1912
|
+
await ctx.close().catch(() => { });
|
|
1913
|
+
throw err;
|
|
1914
|
+
}
|
|
1820
1915
|
return ctx;
|
|
1821
1916
|
}
|
|
1822
1917
|
/**
|
|
@@ -1887,13 +1982,21 @@ export class Browser {
|
|
|
1887
1982
|
const id = params.context;
|
|
1888
1983
|
if (typeof id !== 'string')
|
|
1889
1984
|
return;
|
|
1890
|
-
|
|
1891
|
-
this.
|
|
1985
|
+
const userContext = typeof params.userContext === 'string' ? params.userContext : 'default';
|
|
1986
|
+
this._destroyedTopLevelContextIds.delete(id);
|
|
1987
|
+
const owner = this._wrapContext(userContext);
|
|
1988
|
+
void owner._isInternalPageContext(id).then((internal) => {
|
|
1989
|
+
if (internal || this._destroyedTopLevelContextIds.has(id))
|
|
1990
|
+
return;
|
|
1991
|
+
this._topLevelContextUserContexts.set(id, userContext);
|
|
1992
|
+
this._topLevelContextCacheVersion++;
|
|
1993
|
+
}).catch(() => { });
|
|
1892
1994
|
};
|
|
1893
1995
|
const onDestroyed = (params) => {
|
|
1894
1996
|
if (params.parent)
|
|
1895
1997
|
return;
|
|
1896
1998
|
if (typeof params.context === 'string') {
|
|
1999
|
+
this._destroyedTopLevelContextIds.add(params.context);
|
|
1897
2000
|
this._topLevelContextUserContexts.delete(params.context);
|
|
1898
2001
|
this._topLevelContextCacheVersion++;
|
|
1899
2002
|
}
|
|
@@ -1922,6 +2025,9 @@ export class Browser {
|
|
|
1922
2025
|
}
|
|
1923
2026
|
for (const ctx of tree.contexts ?? []) {
|
|
1924
2027
|
if (!ctx.parent) {
|
|
2028
|
+
const owner = this._wrapContext(ctx.userContext ?? 'default');
|
|
2029
|
+
if (await owner._isInternalPageContext(ctx.context))
|
|
2030
|
+
continue;
|
|
1925
2031
|
this._topLevelContextUserContexts.set(ctx.context, ctx.userContext ?? 'default');
|
|
1926
2032
|
}
|
|
1927
2033
|
}
|
|
@@ -1929,7 +2035,7 @@ export class Browser {
|
|
|
1929
2035
|
}
|
|
1930
2036
|
_firstDefaultTopLevelContext() {
|
|
1931
2037
|
for (const [id, userContext] of this._topLevelContextUserContexts) {
|
|
1932
|
-
if (userContext === 'default')
|
|
2038
|
+
if (userContext === 'default' && !this._isInternalContextId(id))
|
|
1933
2039
|
return id;
|
|
1934
2040
|
}
|
|
1935
2041
|
return undefined;
|
|
@@ -1958,13 +2064,17 @@ export class Browser {
|
|
|
1958
2064
|
const conn = this.bidiSession.getConnection();
|
|
1959
2065
|
const handle = await this.driver.getCurrentWindowHandle().catch(() => '');
|
|
1960
2066
|
await this._ensureTopLevelContextTracking();
|
|
1961
|
-
if (handle &&
|
|
2067
|
+
if (handle &&
|
|
2068
|
+
this._topLevelContextUserContexts.get(handle) === 'default' &&
|
|
2069
|
+
!this._isInternalContextId(handle)) {
|
|
1962
2070
|
return new Page(this.driver, handle, this.getDefaultTimeout, conn, this.defaultContext);
|
|
1963
2071
|
}
|
|
1964
2072
|
// Events should keep the cache warm, but a cheap top-level sync covers
|
|
1965
2073
|
// missed startup events and direct protocol/browser changes.
|
|
1966
2074
|
await this._refreshTopLevelContextCache(conn);
|
|
1967
|
-
if (handle &&
|
|
2075
|
+
if (handle &&
|
|
2076
|
+
this._topLevelContextUserContexts.get(handle) === 'default' &&
|
|
2077
|
+
!this._isInternalContextId(handle)) {
|
|
1968
2078
|
return new Page(this.driver, handle, this.getDefaultTimeout, conn, this.defaultContext);
|
|
1969
2079
|
}
|
|
1970
2080
|
const fallback = this._firstDefaultTopLevelContext();
|