cdp-toolkit 2.6.0 → 3.0.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 +44 -0
- package/CONTRACT.md +1 -1
- package/README.md +81 -46
- package/dist/bidi/mux-daemon.js +47 -0
- package/dist/cli.js +78 -5
- package/dist/index.js +73 -4
- package/dist/mcp.js +236 -50
- package/package.json +5 -3
- package/src/bidi/driver.ts +8 -0
- package/src/cdp/driver.ts +69 -4
- package/src/client.ts +22 -0
- package/src/driver.ts +2 -1
- package/src/frames.ts +169 -0
- package/src/manifest.ts +1 -1
- package/src/mcp.ts +177 -55
- package/src/shared-tools.ts +13 -0
- package/src/toolDocs.ts +3 -3
- package/src/toolGroups.ts +41 -10
- package/src/types.ts +13 -0
- package/src/version.ts +1 -1
package/dist/cli.js
CHANGED
|
@@ -473,6 +473,37 @@ var init_workers = __esm(() => {
|
|
|
473
473
|
WORKER_SELECTOR_PAGE_ONLY_MESSAGE = "target 'worker:<substring>' selects a service/shared worker, and this tool is page-only " + "(closing, selecting or releasing a worker is meaningless). The tools that accept a worker: target are " + `${WORKER_CAPABLE_TOOLS.join(", ")}.`;
|
|
474
474
|
});
|
|
475
475
|
|
|
476
|
+
// src/frames.ts
|
|
477
|
+
function isFrameSelector(selector) {
|
|
478
|
+
return typeof selector === "string" && selector.startsWith(FRAME_SELECTOR_PREFIX);
|
|
479
|
+
}
|
|
480
|
+
function frameNeedle(selector) {
|
|
481
|
+
return selector.slice(FRAME_SELECTOR_PREFIX.length);
|
|
482
|
+
}
|
|
483
|
+
function isFrameTargetType(type) {
|
|
484
|
+
return type === FRAME_TARGET_TYPE;
|
|
485
|
+
}
|
|
486
|
+
function resolveFrameTargets(targets, needle) {
|
|
487
|
+
const liveFrames = targets.filter((t) => isFrameTargetType(t.type));
|
|
488
|
+
return {
|
|
489
|
+
matches: needle === "" ? [] : liveFrames.filter((t) => t.url.includes(needle)),
|
|
490
|
+
liveFrames
|
|
491
|
+
};
|
|
492
|
+
}
|
|
493
|
+
function frameAmbiguityMessage(needle, matches) {
|
|
494
|
+
return `'frame:${needle}' matches more than one out-of-process iframe: ` + matches.map((m) => `${m.url} (${m.id})`).join(", ") + ". Narrow the substring, or pass the iframe's target id directly.";
|
|
495
|
+
}
|
|
496
|
+
function frameMissMessage(needle, liveFrames) {
|
|
497
|
+
const live = liveFrames.length ? ` (out-of-process iframes: ${liveFrames.map((f) => f.url).join(", ")})` : " (no out-of-process iframes are live)";
|
|
498
|
+
return `no out-of-process iframe whose url contains '${needle}'${live}. ` + "Only a CROSS-ORIGIN iframe gets its own process and therefore its own target; a same-origin iframe " + "runs inside its parent page and is NOT a target at all, so no substring can ever select it — reach a " + "same-origin iframe through its parent page instead (evaluate_script on the page, then the iframe " + "element's contentDocument). An out-of-process iframe whose document navigated away is likewise gone " + "from the listing: re-list with list_pages{all:true} and pass the new target id.";
|
|
499
|
+
}
|
|
500
|
+
var FRAME_SELECTOR_PREFIX = "frame:", FRAME_PAGE_ONLY_TOOLS, FRAME_TARGET_TYPE = "iframe", FRAME_EMPTY_NEEDLE_MESSAGE = "'frame:' needs a substring of the iframe's url, e.g. frame:ads.example.com or frame:player.html", FRAME_SELECTOR_UNSUPPORTED_MESSAGE, FRAME_SELECTOR_PAGE_ONLY_MESSAGE;
|
|
501
|
+
var init_frames = __esm(() => {
|
|
502
|
+
FRAME_PAGE_ONLY_TOOLS = ["close_page", "select_page", "release_page", "claim_page"];
|
|
503
|
+
FRAME_SELECTOR_UNSUPPORTED_MESSAGE = "target 'frame:<substring>' selects an out-of-process iframe, which this backend cannot address " + "(WebDriver BiDi has no iframe target — an iframe is a realm inside its tab's browsing context, not a " + "selectable target; Capability 'frame.targets' is Chrome-only). Use --browser chrome to act on a cross-origin iframe.";
|
|
504
|
+
FRAME_SELECTOR_PAGE_ONLY_MESSAGE = "target 'frame:<substring>' selects an out-of-process iframe, and this tool is page-only " + "(closing, selecting, releasing or claiming an iframe is meaningless; these act on the embedder tab). " + `The page-only tools are ${FRAME_PAGE_ONLY_TOOLS.join(", ")}; every other tool that takes a 'target' ` + "accepts a frame: selector.";
|
|
505
|
+
});
|
|
506
|
+
|
|
476
507
|
// src/client.ts
|
|
477
508
|
class CdpConnection {
|
|
478
509
|
wsUrl;
|
|
@@ -671,6 +702,17 @@ async function pickTarget(targets, selector) {
|
|
|
671
702
|
return matches[0];
|
|
672
703
|
throw new CdpError(workerMissMessage(needle, { wakeAttempted: false, liveWorkers }));
|
|
673
704
|
}
|
|
705
|
+
if (isFrameSelector(selector)) {
|
|
706
|
+
const needle = frameNeedle(selector);
|
|
707
|
+
if (needle === "")
|
|
708
|
+
throw new CdpError(FRAME_EMPTY_NEEDLE_MESSAGE);
|
|
709
|
+
const { matches, liveFrames } = resolveFrameTargets(targets, needle);
|
|
710
|
+
if (matches.length > 1)
|
|
711
|
+
throw new CdpError(frameAmbiguityMessage(needle, matches));
|
|
712
|
+
if (matches.length === 1)
|
|
713
|
+
return matches[0];
|
|
714
|
+
throw new CdpError(frameMissMessage(needle, liveFrames));
|
|
715
|
+
}
|
|
674
716
|
const byId = targets.find((t) => t.id === selector);
|
|
675
717
|
if (!byId)
|
|
676
718
|
throw new CdpError(`no target with id '${selector}'`);
|
|
@@ -697,6 +739,7 @@ var init_client = __esm(() => {
|
|
|
697
739
|
init_leases();
|
|
698
740
|
init_origins();
|
|
699
741
|
init_workers();
|
|
742
|
+
init_frames();
|
|
700
743
|
BASE = process.env.CDP_BASE ?? "http://127.0.0.1:9222";
|
|
701
744
|
DEFAULT_TIMEOUT_MS = Number(process.env.CDP_TIMEOUT_MS ?? 15000);
|
|
702
745
|
CdpError = class CdpError extends Error {
|
|
@@ -2844,6 +2887,9 @@ async function pickContext(conn, contexts, selector) {
|
|
|
2844
2887
|
if (isWorkerSelector(selector)) {
|
|
2845
2888
|
throw driverError2("unsupported", WORKER_SELECTOR_UNSUPPORTED_MESSAGE);
|
|
2846
2889
|
}
|
|
2890
|
+
if (isFrameSelector(selector)) {
|
|
2891
|
+
throw driverError2("unsupported", FRAME_SELECTOR_UNSUPPORTED_MESSAGE);
|
|
2892
|
+
}
|
|
2847
2893
|
const exact = contexts.find((c) => c.context === selector);
|
|
2848
2894
|
if (exact)
|
|
2849
2895
|
return exact;
|
|
@@ -3055,6 +3101,7 @@ var init_driver2 = __esm(() => {
|
|
|
3055
3101
|
init_leases();
|
|
3056
3102
|
init_origins();
|
|
3057
3103
|
init_workers();
|
|
3104
|
+
init_frames();
|
|
3058
3105
|
init_activity();
|
|
3059
3106
|
init_client2();
|
|
3060
3107
|
init_session_lease();
|
|
@@ -4162,7 +4209,8 @@ var CDP_CAPABILITIES = new Set([
|
|
|
4162
4209
|
"emulate.focus",
|
|
4163
4210
|
"browser.downloads",
|
|
4164
4211
|
"browser.permissions",
|
|
4165
|
-
"worker.targets"
|
|
4212
|
+
"worker.targets",
|
|
4213
|
+
"frame.targets"
|
|
4166
4214
|
]);
|
|
4167
4215
|
|
|
4168
4216
|
class CdpPageDriver {
|
|
@@ -4295,6 +4343,7 @@ class CdpPageDriver {
|
|
|
4295
4343
|
const interactiveOnly = opts?.interactiveOnly ?? false;
|
|
4296
4344
|
await this.ensureDomain("Accessibility");
|
|
4297
4345
|
const { nodes } = await this.conn.send("Accessibility.getFullAXTree");
|
|
4346
|
+
const oopifFrames = await this.detectOopifFrames();
|
|
4298
4347
|
const byId = new Map;
|
|
4299
4348
|
for (const n of nodes)
|
|
4300
4349
|
byId.set(n.nodeId, n);
|
|
@@ -4303,11 +4352,17 @@ class CdpPageDriver {
|
|
|
4303
4352
|
const walk = (node, depth) => {
|
|
4304
4353
|
const role = axString(node.role);
|
|
4305
4354
|
const name = axString(node.name);
|
|
4306
|
-
const
|
|
4307
|
-
const
|
|
4355
|
+
const backendDOMNodeId = node.backendDOMNodeId;
|
|
4356
|
+
const marker = backendDOMNodeId !== undefined ? oopifFrames.get(backendDOMNodeId) : undefined;
|
|
4357
|
+
const emitted = !node.ignored && backendDOMNodeId !== undefined && (interactiveOnly ? isInteractive(role) || marker !== undefined : !!(role || name));
|
|
4308
4358
|
if (emitted) {
|
|
4309
4359
|
const extras = axExtras(node);
|
|
4310
|
-
|
|
4360
|
+
if (marker) {
|
|
4361
|
+
extras.frame = marker.frameId;
|
|
4362
|
+
if (marker.frameUrl)
|
|
4363
|
+
extras.frameUrl = marker.frameUrl;
|
|
4364
|
+
}
|
|
4365
|
+
out.push({ uid: encodeUid(backendDOMNodeId), role: role ?? "generic", depth, ...name ? { name } : {}, ...Object.keys(extras).length ? { extras } : {} });
|
|
4311
4366
|
}
|
|
4312
4367
|
const childDepth = emitted && !interactiveOnly ? depth + 1 : depth;
|
|
4313
4368
|
for (const childId of node.childIds ?? []) {
|
|
@@ -4320,6 +4375,20 @@ class CdpPageDriver {
|
|
|
4320
4375
|
walk(root, 0);
|
|
4321
4376
|
return out;
|
|
4322
4377
|
}
|
|
4378
|
+
async detectOopifFrames() {
|
|
4379
|
+
const markers = new Map;
|
|
4380
|
+
try {
|
|
4381
|
+
await this.ensureDomainSoft("DOM");
|
|
4382
|
+
const iframeTargets = (await listTargets()).filter((t) => t.type === "iframe");
|
|
4383
|
+
for (const target of iframeTargets) {
|
|
4384
|
+
const owner = await this.conn.send("DOM.getFrameOwner", { frameId: target.id }).catch(() => ({ backendNodeId: undefined }));
|
|
4385
|
+
if (typeof owner.backendNodeId === "number") {
|
|
4386
|
+
markers.set(owner.backendNodeId, { frameId: target.id, ...target.url ? { frameUrl: target.url } : {} });
|
|
4387
|
+
}
|
|
4388
|
+
}
|
|
4389
|
+
} catch {}
|
|
4390
|
+
return markers;
|
|
4391
|
+
}
|
|
4323
4392
|
async locate(loc) {
|
|
4324
4393
|
return encodeUid(await backendNodeIdOf(this.conn, loc));
|
|
4325
4394
|
}
|
|
@@ -5497,6 +5566,7 @@ init_leases();
|
|
|
5497
5566
|
init_activity();
|
|
5498
5567
|
init_origins();
|
|
5499
5568
|
init_workers();
|
|
5569
|
+
init_frames();
|
|
5500
5570
|
var ARTIFACT_DIR = process.env.CDP_ARTIFACT_DIR ?? "/tmp/cdp-toolkit";
|
|
5501
5571
|
var STATE_DIR = process.env.CDP_STATE_DIR ?? "/tmp/cdp-toolkit";
|
|
5502
5572
|
|
|
@@ -5553,6 +5623,9 @@ async function pickPage(driver, pages, selector) {
|
|
|
5553
5623
|
if (isWorkerSelector(selector)) {
|
|
5554
5624
|
throw new SharedToolError(backendOf(driver) === "firefox" ? WORKER_SELECTOR_UNSUPPORTED_MESSAGE : WORKER_SELECTOR_PAGE_ONLY_MESSAGE);
|
|
5555
5625
|
}
|
|
5626
|
+
if (isFrameSelector(selector)) {
|
|
5627
|
+
throw new SharedToolError(backendOf(driver) === "firefox" ? FRAME_SELECTOR_UNSUPPORTED_MESSAGE : FRAME_SELECTOR_PAGE_ONLY_MESSAGE);
|
|
5628
|
+
}
|
|
5556
5629
|
const exactPage = pages.find((x) => x.id === selector);
|
|
5557
5630
|
if (exactPage)
|
|
5558
5631
|
return exactPage;
|
|
@@ -8863,7 +8936,7 @@ var MANIFEST = [
|
|
|
8863
8936
|
},
|
|
8864
8937
|
all: {
|
|
8865
8938
|
type: "boolean",
|
|
8866
|
-
description: "Include non-page targets."
|
|
8939
|
+
description: "Include non-page targets (workers, iframes)."
|
|
8867
8940
|
},
|
|
8868
8941
|
probe: {
|
|
8869
8942
|
type: "boolean",
|
package/dist/index.js
CHANGED
|
@@ -472,6 +472,37 @@ var init_workers = __esm(() => {
|
|
|
472
472
|
WORKER_SELECTOR_PAGE_ONLY_MESSAGE = "target 'worker:<substring>' selects a service/shared worker, and this tool is page-only " + "(closing, selecting or releasing a worker is meaningless). The tools that accept a worker: target are " + `${WORKER_CAPABLE_TOOLS.join(", ")}.`;
|
|
473
473
|
});
|
|
474
474
|
|
|
475
|
+
// src/frames.ts
|
|
476
|
+
function isFrameSelector(selector) {
|
|
477
|
+
return typeof selector === "string" && selector.startsWith(FRAME_SELECTOR_PREFIX);
|
|
478
|
+
}
|
|
479
|
+
function frameNeedle(selector) {
|
|
480
|
+
return selector.slice(FRAME_SELECTOR_PREFIX.length);
|
|
481
|
+
}
|
|
482
|
+
function isFrameTargetType(type) {
|
|
483
|
+
return type === FRAME_TARGET_TYPE;
|
|
484
|
+
}
|
|
485
|
+
function resolveFrameTargets(targets, needle) {
|
|
486
|
+
const liveFrames = targets.filter((t) => isFrameTargetType(t.type));
|
|
487
|
+
return {
|
|
488
|
+
matches: needle === "" ? [] : liveFrames.filter((t) => t.url.includes(needle)),
|
|
489
|
+
liveFrames
|
|
490
|
+
};
|
|
491
|
+
}
|
|
492
|
+
function frameAmbiguityMessage(needle, matches) {
|
|
493
|
+
return `'frame:${needle}' matches more than one out-of-process iframe: ` + matches.map((m) => `${m.url} (${m.id})`).join(", ") + ". Narrow the substring, or pass the iframe's target id directly.";
|
|
494
|
+
}
|
|
495
|
+
function frameMissMessage(needle, liveFrames) {
|
|
496
|
+
const live = liveFrames.length ? ` (out-of-process iframes: ${liveFrames.map((f) => f.url).join(", ")})` : " (no out-of-process iframes are live)";
|
|
497
|
+
return `no out-of-process iframe whose url contains '${needle}'${live}. ` + "Only a CROSS-ORIGIN iframe gets its own process and therefore its own target; a same-origin iframe " + "runs inside its parent page and is NOT a target at all, so no substring can ever select it — reach a " + "same-origin iframe through its parent page instead (evaluate_script on the page, then the iframe " + "element's contentDocument). An out-of-process iframe whose document navigated away is likewise gone " + "from the listing: re-list with list_pages{all:true} and pass the new target id.";
|
|
498
|
+
}
|
|
499
|
+
var FRAME_SELECTOR_PREFIX = "frame:", FRAME_PAGE_ONLY_TOOLS, FRAME_TARGET_TYPE = "iframe", FRAME_EMPTY_NEEDLE_MESSAGE = "'frame:' needs a substring of the iframe's url, e.g. frame:ads.example.com or frame:player.html", FRAME_SELECTOR_UNSUPPORTED_MESSAGE, FRAME_SELECTOR_PAGE_ONLY_MESSAGE;
|
|
500
|
+
var init_frames = __esm(() => {
|
|
501
|
+
FRAME_PAGE_ONLY_TOOLS = ["close_page", "select_page", "release_page", "claim_page"];
|
|
502
|
+
FRAME_SELECTOR_UNSUPPORTED_MESSAGE = "target 'frame:<substring>' selects an out-of-process iframe, which this backend cannot address " + "(WebDriver BiDi has no iframe target — an iframe is a realm inside its tab's browsing context, not a " + "selectable target; Capability 'frame.targets' is Chrome-only). Use --browser chrome to act on a cross-origin iframe.";
|
|
503
|
+
FRAME_SELECTOR_PAGE_ONLY_MESSAGE = "target 'frame:<substring>' selects an out-of-process iframe, and this tool is page-only " + "(closing, selecting, releasing or claiming an iframe is meaningless; these act on the embedder tab). " + `The page-only tools are ${FRAME_PAGE_ONLY_TOOLS.join(", ")}; every other tool that takes a 'target' ` + "accepts a frame: selector.";
|
|
504
|
+
});
|
|
505
|
+
|
|
475
506
|
// src/client.ts
|
|
476
507
|
class CdpConnection {
|
|
477
508
|
wsUrl;
|
|
@@ -670,6 +701,17 @@ async function pickTarget(targets, selector) {
|
|
|
670
701
|
return matches[0];
|
|
671
702
|
throw new CdpError(workerMissMessage(needle, { wakeAttempted: false, liveWorkers }));
|
|
672
703
|
}
|
|
704
|
+
if (isFrameSelector(selector)) {
|
|
705
|
+
const needle = frameNeedle(selector);
|
|
706
|
+
if (needle === "")
|
|
707
|
+
throw new CdpError(FRAME_EMPTY_NEEDLE_MESSAGE);
|
|
708
|
+
const { matches, liveFrames } = resolveFrameTargets(targets, needle);
|
|
709
|
+
if (matches.length > 1)
|
|
710
|
+
throw new CdpError(frameAmbiguityMessage(needle, matches));
|
|
711
|
+
if (matches.length === 1)
|
|
712
|
+
return matches[0];
|
|
713
|
+
throw new CdpError(frameMissMessage(needle, liveFrames));
|
|
714
|
+
}
|
|
673
715
|
const byId = targets.find((t) => t.id === selector);
|
|
674
716
|
if (!byId)
|
|
675
717
|
throw new CdpError(`no target with id '${selector}'`);
|
|
@@ -696,6 +738,7 @@ var init_client = __esm(() => {
|
|
|
696
738
|
init_leases();
|
|
697
739
|
init_origins();
|
|
698
740
|
init_workers();
|
|
741
|
+
init_frames();
|
|
699
742
|
BASE = process.env.CDP_BASE ?? "http://127.0.0.1:9222";
|
|
700
743
|
DEFAULT_TIMEOUT_MS = Number(process.env.CDP_TIMEOUT_MS ?? 15000);
|
|
701
744
|
CdpError = class CdpError extends Error {
|
|
@@ -1564,7 +1607,8 @@ var CDP_CAPABILITIES = new Set([
|
|
|
1564
1607
|
"emulate.focus",
|
|
1565
1608
|
"browser.downloads",
|
|
1566
1609
|
"browser.permissions",
|
|
1567
|
-
"worker.targets"
|
|
1610
|
+
"worker.targets",
|
|
1611
|
+
"frame.targets"
|
|
1568
1612
|
]);
|
|
1569
1613
|
|
|
1570
1614
|
class CdpPageDriver {
|
|
@@ -1697,6 +1741,7 @@ class CdpPageDriver {
|
|
|
1697
1741
|
const interactiveOnly = opts?.interactiveOnly ?? false;
|
|
1698
1742
|
await this.ensureDomain("Accessibility");
|
|
1699
1743
|
const { nodes } = await this.conn.send("Accessibility.getFullAXTree");
|
|
1744
|
+
const oopifFrames = await this.detectOopifFrames();
|
|
1700
1745
|
const byId = new Map;
|
|
1701
1746
|
for (const n of nodes)
|
|
1702
1747
|
byId.set(n.nodeId, n);
|
|
@@ -1705,11 +1750,17 @@ class CdpPageDriver {
|
|
|
1705
1750
|
const walk = (node, depth) => {
|
|
1706
1751
|
const role = axString(node.role);
|
|
1707
1752
|
const name = axString(node.name);
|
|
1708
|
-
const
|
|
1709
|
-
const
|
|
1753
|
+
const backendDOMNodeId = node.backendDOMNodeId;
|
|
1754
|
+
const marker = backendDOMNodeId !== undefined ? oopifFrames.get(backendDOMNodeId) : undefined;
|
|
1755
|
+
const emitted = !node.ignored && backendDOMNodeId !== undefined && (interactiveOnly ? isInteractive(role) || marker !== undefined : !!(role || name));
|
|
1710
1756
|
if (emitted) {
|
|
1711
1757
|
const extras = axExtras(node);
|
|
1712
|
-
|
|
1758
|
+
if (marker) {
|
|
1759
|
+
extras.frame = marker.frameId;
|
|
1760
|
+
if (marker.frameUrl)
|
|
1761
|
+
extras.frameUrl = marker.frameUrl;
|
|
1762
|
+
}
|
|
1763
|
+
out.push({ uid: encodeUid(backendDOMNodeId), role: role ?? "generic", depth, ...name ? { name } : {}, ...Object.keys(extras).length ? { extras } : {} });
|
|
1713
1764
|
}
|
|
1714
1765
|
const childDepth = emitted && !interactiveOnly ? depth + 1 : depth;
|
|
1715
1766
|
for (const childId of node.childIds ?? []) {
|
|
@@ -1722,6 +1773,20 @@ class CdpPageDriver {
|
|
|
1722
1773
|
walk(root, 0);
|
|
1723
1774
|
return out;
|
|
1724
1775
|
}
|
|
1776
|
+
async detectOopifFrames() {
|
|
1777
|
+
const markers = new Map;
|
|
1778
|
+
try {
|
|
1779
|
+
await this.ensureDomainSoft("DOM");
|
|
1780
|
+
const iframeTargets = (await listTargets()).filter((t) => t.type === "iframe");
|
|
1781
|
+
for (const target of iframeTargets) {
|
|
1782
|
+
const owner = await this.conn.send("DOM.getFrameOwner", { frameId: target.id }).catch(() => ({ backendNodeId: undefined }));
|
|
1783
|
+
if (typeof owner.backendNodeId === "number") {
|
|
1784
|
+
markers.set(owner.backendNodeId, { frameId: target.id, ...target.url ? { frameUrl: target.url } : {} });
|
|
1785
|
+
}
|
|
1786
|
+
}
|
|
1787
|
+
} catch {}
|
|
1788
|
+
return markers;
|
|
1789
|
+
}
|
|
1725
1790
|
async locate(loc) {
|
|
1726
1791
|
return encodeUid(await backendNodeIdOf(this.conn, loc));
|
|
1727
1792
|
}
|
|
@@ -2899,6 +2964,7 @@ init_leases();
|
|
|
2899
2964
|
init_activity();
|
|
2900
2965
|
init_origins();
|
|
2901
2966
|
init_workers();
|
|
2967
|
+
init_frames();
|
|
2902
2968
|
var ARTIFACT_DIR = process.env.CDP_ARTIFACT_DIR ?? "/tmp/cdp-toolkit";
|
|
2903
2969
|
var STATE_DIR = process.env.CDP_STATE_DIR ?? "/tmp/cdp-toolkit";
|
|
2904
2970
|
|
|
@@ -2955,6 +3021,9 @@ async function pickPage(driver, pages, selector) {
|
|
|
2955
3021
|
if (isWorkerSelector(selector)) {
|
|
2956
3022
|
throw new SharedToolError(backendOf(driver) === "firefox" ? WORKER_SELECTOR_UNSUPPORTED_MESSAGE : WORKER_SELECTOR_PAGE_ONLY_MESSAGE);
|
|
2957
3023
|
}
|
|
3024
|
+
if (isFrameSelector(selector)) {
|
|
3025
|
+
throw new SharedToolError(backendOf(driver) === "firefox" ? FRAME_SELECTOR_UNSUPPORTED_MESSAGE : FRAME_SELECTOR_PAGE_ONLY_MESSAGE);
|
|
3026
|
+
}
|
|
2958
3027
|
const exactPage = pages.find((x) => x.id === selector);
|
|
2959
3028
|
if (exactPage)
|
|
2960
3029
|
return exactPage;
|