@browserbasehq/orca 3.2.1-preview.2 → 3.4.0-preview.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/cjs/lib/inference.js +2 -9
- package/dist/cjs/lib/inference.js.map +1 -1
- package/dist/cjs/lib/prompt.js +3 -1
- package/dist/cjs/lib/prompt.js.map +1 -1
- package/dist/cjs/lib/v3/agent/AgentProvider.js +3 -0
- package/dist/cjs/lib/v3/agent/AgentProvider.js.map +1 -1
- package/dist/cjs/lib/v3/agent/tools/fillFormVision.js +16 -12
- package/dist/cjs/lib/v3/agent/tools/fillFormVision.js.map +1 -1
- package/dist/cjs/lib/v3/agent/utils/validateExperimentalFeatures.js +0 -4
- package/dist/cjs/lib/v3/agent/utils/validateExperimentalFeatures.js.map +1 -1
- package/dist/cjs/lib/v3/api.d.ts +1 -0
- package/dist/cjs/lib/v3/api.js +15 -3
- package/dist/cjs/lib/v3/api.js.map +1 -1
- package/dist/cjs/lib/v3/dom/build/a11yScripts.generated.js +1 -1
- package/dist/cjs/lib/v3/dom/build/a11yScripts.generated.js.map +1 -1
- package/dist/cjs/lib/v3/dom/build/locatorScripts.generated.js +1 -1
- package/dist/cjs/lib/v3/dom/build/locatorScripts.generated.js.map +1 -1
- package/dist/cjs/lib/v3/dom/build/reRenderScriptContent.d.ts +1 -1
- package/dist/cjs/lib/v3/dom/build/reRenderScriptContent.js +1 -1
- package/dist/cjs/lib/v3/dom/build/reRenderScriptContent.js.map +1 -1
- package/dist/cjs/lib/v3/dom/build/screenshotScripts.generated.js +1 -1
- package/dist/cjs/lib/v3/dom/build/screenshotScripts.generated.js.map +1 -1
- package/dist/cjs/lib/v3/dom/build/scriptV3Content.d.ts +1 -1
- package/dist/cjs/lib/v3/dom/build/scriptV3Content.js +1 -1
- package/dist/cjs/lib/v3/dom/build/scriptV3Content.js.map +1 -1
- package/dist/cjs/lib/v3/handlers/extractHandler.js +3 -1
- package/dist/cjs/lib/v3/handlers/extractHandler.js.map +1 -1
- package/dist/cjs/lib/v3/handlers/v3AgentHandler.js +3 -5
- package/dist/cjs/lib/v3/handlers/v3AgentHandler.js.map +1 -1
- package/dist/cjs/lib/v3/index.d.ts +1 -1
- package/dist/cjs/lib/v3/llm/CerebrasClient.js +1 -1
- package/dist/cjs/lib/v3/llm/CerebrasClient.js.map +1 -1
- package/dist/cjs/lib/v3/llm/GroqClient.js +1 -1
- package/dist/cjs/lib/v3/llm/GroqClient.js.map +1 -1
- package/dist/cjs/lib/v3/types/private/agent.d.ts +5 -0
- package/dist/cjs/lib/v3/types/private/agent.js +11 -0
- package/dist/cjs/lib/v3/types/private/agent.js.map +1 -1
- package/dist/cjs/lib/v3/types/private/handlers.d.ts +1 -0
- package/dist/cjs/lib/v3/types/private/handlers.js.map +1 -1
- package/dist/cjs/lib/v3/types/private/snapshot.d.ts +8 -0
- package/dist/cjs/lib/v3/types/private/snapshot.js.map +1 -1
- package/dist/cjs/lib/v3/types/public/agent.d.ts +2 -3
- package/dist/cjs/lib/v3/types/public/agent.js +3 -0
- package/dist/cjs/lib/v3/types/public/agent.js.map +1 -1
- package/dist/cjs/lib/v3/types/public/api.d.ts +5 -1
- package/dist/cjs/lib/v3/types/public/api.js +15 -2
- package/dist/cjs/lib/v3/types/public/api.js.map +1 -1
- package/dist/cjs/lib/v3/types/public/methods.d.ts +1 -0
- package/dist/cjs/lib/v3/types/public/methods.js.map +1 -1
- package/dist/cjs/lib/v3/understudy/a11y/snapshot/a11yTree.js +21 -12
- package/dist/cjs/lib/v3/understudy/a11y/snapshot/a11yTree.js.map +1 -1
- package/dist/cjs/lib/v3/understudy/a11y/snapshot/capture.d.ts +11 -2
- package/dist/cjs/lib/v3/understudy/a11y/snapshot/capture.js +268 -21
- package/dist/cjs/lib/v3/understudy/a11y/snapshot/capture.js.map +1 -1
- package/dist/cjs/lib/v3/understudy/a11y/snapshot/domTree.js +60 -7
- package/dist/cjs/lib/v3/understudy/a11y/snapshot/domTree.js.map +1 -1
- package/dist/cjs/lib/v3/understudy/frameRegistry.js +16 -5
- package/dist/cjs/lib/v3/understudy/frameRegistry.js.map +1 -1
- package/dist/cjs/lib/v3/v3.d.ts +1 -0
- package/dist/cjs/lib/v3/v3.js +15 -14
- package/dist/cjs/lib/v3/v3.js.map +1 -1
- package/dist/cjs/lib/version.d.ts +1 -1
- package/dist/cjs/lib/version.js +1 -1
- package/dist/cjs/lib/version.js.map +1 -1
- package/dist/cjs/tests/integration/observe-element-id-format.spec.js +130 -0
- package/dist/cjs/tests/integration/observe-element-id-format.spec.js.map +1 -0
- package/dist/cjs/tests/unit/agent-mode-routing.test.js +88 -0
- package/dist/cjs/tests/unit/agent-mode-routing.test.js.map +1 -0
- package/dist/cjs/tests/unit/agent-temperature.test.d.ts +1 -0
- package/dist/cjs/tests/unit/agent-temperature.test.js +191 -0
- package/dist/cjs/tests/unit/agent-temperature.test.js.map +1 -0
- package/dist/cjs/tests/unit/agent-variables-validation.test.d.ts +1 -0
- package/dist/cjs/tests/unit/agent-variables-validation.test.js +43 -0
- package/dist/cjs/tests/unit/agent-variables-validation.test.js.map +1 -0
- package/dist/cjs/tests/unit/api-client-observe-variables.test.js +47 -0
- package/dist/cjs/tests/unit/api-client-observe-variables.test.js.map +1 -1
- package/dist/cjs/tests/unit/api-optional-model-api-key.test.js +60 -0
- package/dist/cjs/tests/unit/api-optional-model-api-key.test.js.map +1 -1
- package/dist/cjs/tests/unit/api-variables-schema.test.js +25 -0
- package/dist/cjs/tests/unit/api-variables-schema.test.js.map +1 -1
- package/dist/cjs/tests/unit/frame-registry-oopif-adoption.test.d.ts +1 -0
- package/dist/cjs/tests/unit/frame-registry-oopif-adoption.test.js +60 -0
- package/dist/cjs/tests/unit/frame-registry-oopif-adoption.test.js.map +1 -0
- package/dist/cjs/tests/unit/inference-temperature.test.d.ts +1 -0
- package/dist/cjs/tests/unit/inference-temperature.test.js +65 -0
- package/dist/cjs/tests/unit/inference-temperature.test.js.map +1 -0
- package/dist/cjs/tests/unit/openai-compatible-temperature.test.d.ts +1 -0
- package/dist/cjs/tests/unit/openai-compatible-temperature.test.js +84 -0
- package/dist/cjs/tests/unit/openai-compatible-temperature.test.js.map +1 -0
- package/dist/cjs/tests/unit/prompt-observe-variables.test.js +6 -0
- package/dist/cjs/tests/unit/prompt-observe-variables.test.js.map +1 -1
- package/dist/cjs/tests/unit/public-api/llm-and-agents.test.js +3 -0
- package/dist/cjs/tests/unit/public-api/llm-and-agents.test.js.map +1 -1
- package/dist/cjs/tests/unit/public-api/public-types.test.js.map +1 -1
- package/dist/cjs/tests/unit/snapshot-a11y-resolvers.test.js +106 -5
- package/dist/cjs/tests/unit/snapshot-a11y-resolvers.test.js.map +1 -1
- package/dist/cjs/tests/unit/snapshot-a11y-tree-utils.test.js +20 -0
- package/dist/cjs/tests/unit/snapshot-a11y-tree-utils.test.js.map +1 -1
- package/dist/cjs/tests/unit/snapshot-capture-orchestration.test.js +119 -9
- package/dist/cjs/tests/unit/snapshot-capture-orchestration.test.js.map +1 -1
- package/dist/esm/lib/inference.js +2 -9
- package/dist/esm/lib/inference.js.map +1 -1
- package/dist/esm/lib/prompt.js +3 -1
- package/dist/esm/lib/prompt.js.map +1 -1
- package/dist/esm/lib/v3/agent/AgentProvider.js +3 -0
- package/dist/esm/lib/v3/agent/AgentProvider.js.map +1 -1
- package/dist/esm/lib/v3/agent/tools/fillFormVision.js +16 -12
- package/dist/esm/lib/v3/agent/tools/fillFormVision.js.map +1 -1
- package/dist/esm/lib/v3/agent/utils/validateExperimentalFeatures.js +0 -4
- package/dist/esm/lib/v3/agent/utils/validateExperimentalFeatures.js.map +1 -1
- package/dist/esm/lib/v3/api.d.ts +1 -0
- package/dist/esm/lib/v3/api.js +15 -3
- package/dist/esm/lib/v3/api.js.map +1 -1
- package/dist/esm/lib/v3/dom/build/a11yScripts.generated.js +1 -1
- package/dist/esm/lib/v3/dom/build/a11yScripts.generated.js.map +1 -1
- package/dist/esm/lib/v3/dom/build/locatorScripts.generated.js +1 -1
- package/dist/esm/lib/v3/dom/build/locatorScripts.generated.js.map +1 -1
- package/dist/esm/lib/v3/dom/build/reRenderScriptContent.d.ts +1 -1
- package/dist/esm/lib/v3/dom/build/reRenderScriptContent.js +1 -1
- package/dist/esm/lib/v3/dom/build/reRenderScriptContent.js.map +1 -1
- package/dist/esm/lib/v3/dom/build/screenshotScripts.generated.js +1 -1
- package/dist/esm/lib/v3/dom/build/screenshotScripts.generated.js.map +1 -1
- package/dist/esm/lib/v3/dom/build/scriptV3Content.d.ts +1 -1
- package/dist/esm/lib/v3/dom/build/scriptV3Content.js +1 -1
- package/dist/esm/lib/v3/dom/build/scriptV3Content.js.map +1 -1
- package/dist/esm/lib/v3/handlers/extractHandler.js +3 -1
- package/dist/esm/lib/v3/handlers/extractHandler.js.map +1 -1
- package/dist/esm/lib/v3/handlers/v3AgentHandler.js +3 -5
- package/dist/esm/lib/v3/handlers/v3AgentHandler.js.map +1 -1
- package/dist/esm/lib/v3/index.d.ts +1 -1
- package/dist/esm/lib/v3/llm/CerebrasClient.js +1 -1
- package/dist/esm/lib/v3/llm/CerebrasClient.js.map +1 -1
- package/dist/esm/lib/v3/llm/GroqClient.js +1 -1
- package/dist/esm/lib/v3/llm/GroqClient.js.map +1 -1
- package/dist/esm/lib/v3/types/private/agent.d.ts +5 -0
- package/dist/esm/lib/v3/types/private/agent.js +10 -1
- package/dist/esm/lib/v3/types/private/agent.js.map +1 -1
- package/dist/esm/lib/v3/types/private/handlers.d.ts +1 -0
- package/dist/esm/lib/v3/types/private/handlers.js.map +1 -1
- package/dist/esm/lib/v3/types/private/snapshot.d.ts +8 -0
- package/dist/esm/lib/v3/types/private/snapshot.js.map +1 -1
- package/dist/esm/lib/v3/types/public/agent.d.ts +2 -3
- package/dist/esm/lib/v3/types/public/agent.js +3 -0
- package/dist/esm/lib/v3/types/public/agent.js.map +1 -1
- package/dist/esm/lib/v3/types/public/api.d.ts +5 -1
- package/dist/esm/lib/v3/types/public/api.js +15 -2
- package/dist/esm/lib/v3/types/public/api.js.map +1 -1
- package/dist/esm/lib/v3/types/public/methods.d.ts +1 -0
- package/dist/esm/lib/v3/types/public/methods.js.map +1 -1
- package/dist/esm/lib/v3/understudy/a11y/snapshot/a11yTree.js +21 -12
- package/dist/esm/lib/v3/understudy/a11y/snapshot/a11yTree.js.map +1 -1
- package/dist/esm/lib/v3/understudy/a11y/snapshot/capture.d.ts +11 -2
- package/dist/esm/lib/v3/understudy/a11y/snapshot/capture.js +267 -22
- package/dist/esm/lib/v3/understudy/a11y/snapshot/capture.js.map +1 -1
- package/dist/esm/lib/v3/understudy/a11y/snapshot/domTree.js +60 -7
- package/dist/esm/lib/v3/understudy/a11y/snapshot/domTree.js.map +1 -1
- package/dist/esm/lib/v3/understudy/frameRegistry.js +16 -5
- package/dist/esm/lib/v3/understudy/frameRegistry.js.map +1 -1
- package/dist/esm/lib/v3/v3.d.ts +1 -0
- package/dist/esm/lib/v3/v3.js +15 -14
- package/dist/esm/lib/v3/v3.js.map +1 -1
- package/dist/esm/lib/version.d.ts +1 -1
- package/dist/esm/lib/version.js +1 -1
- package/dist/esm/lib/version.js.map +1 -1
- package/dist/esm/tests/integration/observe-element-id-format.spec.d.ts +1 -0
- package/dist/esm/tests/integration/observe-element-id-format.spec.js +128 -0
- package/dist/esm/tests/integration/observe-element-id-format.spec.js.map +1 -0
- package/dist/esm/tests/unit/agent-mode-routing.test.d.ts +1 -0
- package/dist/esm/tests/unit/agent-mode-routing.test.js +86 -0
- package/dist/esm/tests/unit/agent-mode-routing.test.js.map +1 -0
- package/dist/esm/tests/unit/agent-temperature.test.d.ts +1 -0
- package/dist/esm/tests/unit/agent-temperature.test.js +189 -0
- package/dist/esm/tests/unit/agent-temperature.test.js.map +1 -0
- package/dist/esm/tests/unit/agent-variables-validation.test.d.ts +1 -0
- package/dist/esm/tests/unit/agent-variables-validation.test.js +41 -0
- package/dist/esm/tests/unit/agent-variables-validation.test.js.map +1 -0
- package/dist/esm/tests/unit/api-client-observe-variables.test.js +47 -0
- package/dist/esm/tests/unit/api-client-observe-variables.test.js.map +1 -1
- package/dist/esm/tests/unit/api-optional-model-api-key.test.js +60 -0
- package/dist/esm/tests/unit/api-optional-model-api-key.test.js.map +1 -1
- package/dist/esm/tests/unit/api-variables-schema.test.js +25 -0
- package/dist/esm/tests/unit/api-variables-schema.test.js.map +1 -1
- package/dist/esm/tests/unit/frame-registry-oopif-adoption.test.d.ts +1 -0
- package/dist/esm/tests/unit/frame-registry-oopif-adoption.test.js +58 -0
- package/dist/esm/tests/unit/frame-registry-oopif-adoption.test.js.map +1 -0
- package/dist/esm/tests/unit/inference-temperature.test.d.ts +1 -0
- package/dist/esm/tests/unit/inference-temperature.test.js +63 -0
- package/dist/esm/tests/unit/inference-temperature.test.js.map +1 -0
- package/dist/esm/tests/unit/openai-compatible-temperature.test.d.ts +1 -0
- package/dist/esm/tests/unit/openai-compatible-temperature.test.js +82 -0
- package/dist/esm/tests/unit/openai-compatible-temperature.test.js.map +1 -0
- package/dist/esm/tests/unit/prompt-observe-variables.test.js +6 -0
- package/dist/esm/tests/unit/prompt-observe-variables.test.js.map +1 -1
- package/dist/esm/tests/unit/public-api/llm-and-agents.test.js +3 -0
- package/dist/esm/tests/unit/public-api/llm-and-agents.test.js.map +1 -1
- package/dist/esm/tests/unit/public-api/public-types.test.js.map +1 -1
- package/dist/esm/tests/unit/snapshot-a11y-resolvers.test.js +106 -5
- package/dist/esm/tests/unit/snapshot-a11y-resolvers.test.js.map +1 -1
- package/dist/esm/tests/unit/snapshot-a11y-tree-utils.test.js +20 -0
- package/dist/esm/tests/unit/snapshot-a11y-tree-utils.test.js.map +1 -1
- package/dist/esm/tests/unit/snapshot-capture-orchestration.test.js +119 -9
- package/dist/esm/tests/unit/snapshot-capture-orchestration.test.js.map +1 -1
- package/package.json +1 -1
- package/dist/cjs/tests/integration/agent-captcha-autosolve.spec.js +0 -56
- package/dist/cjs/tests/integration/agent-captcha-autosolve.spec.js.map +0 -1
- package/dist/esm/tests/integration/agent-captcha-autosolve.spec.js +0 -54
- package/dist/esm/tests/integration/agent-captcha-autosolve.spec.js.map +0 -1
- /package/dist/cjs/tests/integration/{agent-captcha-autosolve.spec.d.ts → observe-element-id-format.spec.d.ts} +0 -0
- /package/dist/{esm/tests/integration/agent-captcha-autosolve.spec.d.ts → cjs/tests/unit/agent-mode-routing.test.d.ts} +0 -0
|
@@ -22,17 +22,6 @@ export async function a11yForFrame(session, frameId, opts) {
|
|
|
22
22
|
throw e;
|
|
23
23
|
({ nodes } = await session.send("Accessibility.getFullAXTree"));
|
|
24
24
|
}
|
|
25
|
-
const urlMap = {};
|
|
26
|
-
for (const n of nodes) {
|
|
27
|
-
const be = n.backendDOMNodeId;
|
|
28
|
-
if (typeof be !== "number")
|
|
29
|
-
continue;
|
|
30
|
-
const url = extractUrlFromAXNode(n);
|
|
31
|
-
if (!url)
|
|
32
|
-
continue;
|
|
33
|
-
const enc = opts.encode(be);
|
|
34
|
-
urlMap[enc] = url;
|
|
35
|
-
}
|
|
36
25
|
let scopeApplied = false;
|
|
37
26
|
const nodesForOutline = await (async () => {
|
|
38
27
|
const sel = opts.focusSelector?.trim();
|
|
@@ -74,7 +63,22 @@ export async function a11yForFrame(session, frameId, opts) {
|
|
|
74
63
|
return nodes;
|
|
75
64
|
}
|
|
76
65
|
})();
|
|
77
|
-
const
|
|
66
|
+
const filteredNodes = nodesForOutline.filter((node) => {
|
|
67
|
+
const be = node.backendDOMNodeId;
|
|
68
|
+
return typeof be !== "number" || !opts.isIgnoredBackendNode?.(be);
|
|
69
|
+
});
|
|
70
|
+
const urlMap = {};
|
|
71
|
+
for (const n of filteredNodes) {
|
|
72
|
+
const be = n.backendDOMNodeId;
|
|
73
|
+
if (typeof be !== "number")
|
|
74
|
+
continue;
|
|
75
|
+
const url = extractUrlFromAXNode(n);
|
|
76
|
+
if (!url)
|
|
77
|
+
continue;
|
|
78
|
+
const enc = opts.encode(be);
|
|
79
|
+
urlMap[enc] = url;
|
|
80
|
+
}
|
|
81
|
+
const decorated = decorateRoles(filteredNodes, opts);
|
|
78
82
|
const { tree } = await buildHierarchicalTree(decorated, opts);
|
|
79
83
|
const simplified = tree.map((n) => formatTreeLine(n)).join("\n");
|
|
80
84
|
return { outline: simplified.trimEnd(), urlMap, scopeApplied };
|
|
@@ -103,6 +107,11 @@ export function decorateRoles(nodes, opts) {
|
|
|
103
107
|
? `scrollable, ${tagLabel}`
|
|
104
108
|
: `scrollable${role ? `, ${role}` : ""}`;
|
|
105
109
|
}
|
|
110
|
+
// File inputs typically get role "button" from Chrome's AX tree;
|
|
111
|
+
// override so they appear as "input, file" in the outline.
|
|
112
|
+
if (tag === "input, file") {
|
|
113
|
+
role = tag;
|
|
114
|
+
}
|
|
106
115
|
return {
|
|
107
116
|
role,
|
|
108
117
|
name: n.name?.value,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"a11yTree.js","sourceRoot":"","sources":["../../../../../../../lib/v3/understudy/a11y/snapshot/a11yTree.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Protocol } from \"devtools-protocol\";\nimport type { CDPSessionLike } from \"../../cdp.js\";\nimport type {\n A11yNode,\n A11yOptions,\n AccessibilityTreeResult,\n} from \"../../../types/private/snapshot.js\";\nimport {\n resolveObjectIdForCss,\n resolveObjectIdForXPath,\n} from \"./focusSelectors.js\";\nimport { formatTreeLine, normaliseSpaces } from \"./treeFormatUtils.js\";\n\n/**\n * Fetch and prune the accessibility tree for a frame, optionally scoping the\n * output to a selector root for faster targeted snapshots.\n */\nexport async function a11yForFrame(\n session: CDPSessionLike,\n frameId: string | undefined,\n opts: A11yOptions,\n): Promise<AccessibilityTreeResult> {\n await session.send(\"Accessibility.enable\").catch(() => {});\n await session.send(\"Runtime.enable\").catch(() => {});\n await session.send(\"DOM.enable\").catch(() => {});\n\n let nodes: Protocol.Accessibility.AXNode[] = [];\n try {\n const params = frameId ? ({ frameId } as Record<string, unknown>) : {};\n ({ nodes } = await session.send<{\n nodes: Protocol.Accessibility.AXNode[];\n }>(\"Accessibility.getFullAXTree\", params));\n } catch (e) {\n const msg = String((e as Error)?.message ?? e ?? \"\");\n const isFrameScopeError =\n msg.includes(\"Frame with the given\") ||\n msg.includes(\"does not belong to the target\") ||\n msg.includes(\"is not found\");\n if (!isFrameScopeError || !frameId) throw e;\n ({ nodes } = await session.send<{\n nodes: Protocol.Accessibility.AXNode[];\n }>(\"Accessibility.getFullAXTree\"));\n }\n\n const urlMap: Record<string, string> = {};\n for (const n of nodes) {\n const be = n.backendDOMNodeId;\n if (typeof be !== \"number\") continue;\n const url = extractUrlFromAXNode(n);\n if (!url) continue;\n const enc = opts.encode(be);\n urlMap[enc] = url;\n }\n\n let scopeApplied = false;\n const nodesForOutline = await (async () => {\n const sel = opts.focusSelector?.trim();\n if (!sel) return nodes;\n try {\n const looksLikeXPath = /^xpath=/i.test(sel) || sel.startsWith(\"/\");\n const objectId = looksLikeXPath\n ? await resolveObjectIdForXPath(session, sel, frameId)\n : await resolveObjectIdForCss(session, sel, frameId);\n if (!objectId) return nodes;\n const desc = await session.send<{ node?: { backendNodeId?: number } }>(\n \"DOM.describeNode\",\n { objectId },\n );\n const be = desc.node?.backendNodeId;\n if (typeof be !== \"number\") return nodes;\n const target = nodes.find((n) => n.backendDOMNodeId === be);\n if (!target) return nodes;\n scopeApplied = true;\n const keep = new Set<string>([target.nodeId]);\n const queue: Protocol.Accessibility.AXNode[] = [target];\n while (queue.length) {\n const cur = queue.shift()!;\n for (const id of cur.childIds ?? []) {\n if (keep.has(id)) continue;\n keep.add(id);\n const child = nodes.find((n) => n.nodeId === id);\n if (child) queue.push(child);\n }\n }\n return nodes\n .filter((n) => keep.has(n.nodeId))\n .map((n) =>\n n.nodeId === target.nodeId ? { ...n, parentId: undefined } : n,\n );\n } catch {\n return nodes;\n }\n })();\n\n const decorated = decorateRoles(nodesForOutline, opts);\n const { tree } = await buildHierarchicalTree(decorated, opts);\n\n const simplified = tree.map((n) => formatTreeLine(n)).join(\"\\n\");\n return { outline: simplified.trimEnd(), urlMap, scopeApplied };\n}\n\nexport function decorateRoles(\n nodes: Protocol.Accessibility.AXNode[],\n opts: A11yOptions,\n): A11yNode[] {\n const asRole = (n: Protocol.Accessibility.AXNode) =>\n String(n.role?.value ?? \"\");\n\n return nodes.map((n) => {\n let encodedId: string | undefined;\n if (typeof n.backendDOMNodeId === \"number\") {\n try {\n encodedId = opts.encode(n.backendDOMNodeId);\n } catch {\n //\n }\n }\n\n let role = asRole(n);\n\n const domIsScrollable = encodedId\n ? opts.scrollableMap[encodedId] === true\n : false;\n const tag = encodedId ? opts.tagNameMap[encodedId] : undefined;\n const isHtmlElement = tag === \"html\";\n if ((domIsScrollable || isHtmlElement) && tag !== \"#document\") {\n const tagLabel = tag && tag.startsWith(\"#\") ? tag.slice(1) : tag;\n role = tagLabel\n ? `scrollable, ${tagLabel}`\n : `scrollable${role ? `, ${role}` : \"\"}`;\n }\n\n return {\n role,\n name: n.name?.value,\n description: n.description?.value,\n value: n.value?.value,\n nodeId: n.nodeId,\n backendDOMNodeId: n.backendDOMNodeId,\n parentId: n.parentId,\n childIds: n.childIds,\n encodedId,\n };\n });\n}\n\nexport async function buildHierarchicalTree(\n nodes: A11yNode[],\n opts: A11yOptions,\n): Promise<{ tree: A11yNode[] }> {\n const nodeMap = new Map<string, A11yNode>();\n\n for (const n of nodes) {\n const keep =\n !!(n.name && n.name.trim()) ||\n !!(n.childIds && n.childIds.length) ||\n !isStructural(n.role);\n if (!keep) continue;\n nodeMap.set(n.nodeId, { ...n });\n }\n\n for (const n of nodes) {\n if (!n.parentId) continue;\n const parent = nodeMap.get(n.parentId);\n const cur = nodeMap.get(n.nodeId);\n if (parent && cur) (parent.children ??= []).push(cur);\n }\n\n const roots = nodes\n .filter((n) => !n.parentId && nodeMap.has(n.nodeId))\n .map((n) => nodeMap.get(n.nodeId)!) as A11yNode[];\n\n const cleaned = (await Promise.all(roots.map(pruneStructuralSafe))).filter(\n Boolean,\n ) as A11yNode[];\n\n return { tree: cleaned };\n\n async function pruneStructuralSafe(node: A11yNode): Promise<A11yNode | null> {\n if (+node.nodeId < 0) return null;\n\n const children = node.children ?? [];\n if (!children.length) {\n return isStructural(node.role) ? null : node;\n }\n\n const cleanedKids = (\n await Promise.all(children.map(pruneStructuralSafe))\n ).filter(Boolean) as A11yNode[];\n\n const prunedStatic = removeRedundantStaticTextChildren(node, cleanedKids);\n\n if (isStructural(node.role)) {\n if (prunedStatic.length === 1) return prunedStatic[0]!;\n if (prunedStatic.length === 0) return null;\n }\n\n let newRole = node.role;\n if ((newRole === \"generic\" || newRole === \"none\") && node.encodedId) {\n const tagName = opts.tagNameMap[node.encodedId];\n if (tagName) newRole = tagName;\n }\n\n if (newRole === \"combobox\" && node.encodedId) {\n const tagName = opts.tagNameMap[node.encodedId];\n if (tagName === \"select\") newRole = \"select\";\n }\n\n return { ...node, role: newRole, children: prunedStatic };\n }\n}\n\nexport function isStructural(role: string): boolean {\n const r = role?.toLowerCase();\n return r === \"generic\" || r === \"none\" || r === \"inlinetextbox\";\n}\n\nexport function extractUrlFromAXNode(\n ax: Protocol.Accessibility.AXNode,\n): string | undefined {\n const props = ax.properties ?? [];\n const urlProp = props.find((p) => p.name === \"url\");\n const value = urlProp?.value?.value;\n return typeof value === \"string\" && value.trim() ? value.trim() : undefined;\n}\n\nexport function removeRedundantStaticTextChildren(\n parent: A11yNode,\n children: A11yNode[],\n): A11yNode[] {\n if (!parent.name) return children;\n const parentNorm = normaliseSpaces(parent.name).trim();\n let combined = \"\";\n for (const c of children) {\n if (c.role === \"StaticText\" && c.name) {\n combined += normaliseSpaces(c.name).trim();\n }\n }\n if (combined === parentNorm) {\n return children.filter((c) => c.role !== \"StaticText\");\n }\n return children;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"a11yTree.js","sourceRoot":"","sources":["../../../../../../../lib/v3/understudy/a11y/snapshot/a11yTree.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Protocol } from \"devtools-protocol\";\nimport type { CDPSessionLike } from \"../../cdp.js\";\nimport type {\n A11yNode,\n A11yOptions,\n AccessibilityTreeResult,\n} from \"../../../types/private/snapshot.js\";\nimport {\n resolveObjectIdForCss,\n resolveObjectIdForXPath,\n} from \"./focusSelectors.js\";\nimport { formatTreeLine, normaliseSpaces } from \"./treeFormatUtils.js\";\n\n/**\n * Fetch and prune the accessibility tree for a frame, optionally scoping the\n * output to a selector root for faster targeted snapshots.\n */\nexport async function a11yForFrame(\n session: CDPSessionLike,\n frameId: string | undefined,\n opts: A11yOptions,\n): Promise<AccessibilityTreeResult> {\n await session.send(\"Accessibility.enable\").catch(() => {});\n await session.send(\"Runtime.enable\").catch(() => {});\n await session.send(\"DOM.enable\").catch(() => {});\n\n let nodes: Protocol.Accessibility.AXNode[] = [];\n try {\n const params = frameId ? ({ frameId } as Record<string, unknown>) : {};\n ({ nodes } = await session.send<{\n nodes: Protocol.Accessibility.AXNode[];\n }>(\"Accessibility.getFullAXTree\", params));\n } catch (e) {\n const msg = String((e as Error)?.message ?? e ?? \"\");\n const isFrameScopeError =\n msg.includes(\"Frame with the given\") ||\n msg.includes(\"does not belong to the target\") ||\n msg.includes(\"is not found\");\n if (!isFrameScopeError || !frameId) throw e;\n ({ nodes } = await session.send<{\n nodes: Protocol.Accessibility.AXNode[];\n }>(\"Accessibility.getFullAXTree\"));\n }\n\n let scopeApplied = false;\n const nodesForOutline = await (async () => {\n const sel = opts.focusSelector?.trim();\n if (!sel) return nodes;\n try {\n const looksLikeXPath = /^xpath=/i.test(sel) || sel.startsWith(\"/\");\n const objectId = looksLikeXPath\n ? await resolveObjectIdForXPath(session, sel, frameId)\n : await resolveObjectIdForCss(session, sel, frameId);\n if (!objectId) return nodes;\n const desc = await session.send<{ node?: { backendNodeId?: number } }>(\n \"DOM.describeNode\",\n { objectId },\n );\n const be = desc.node?.backendNodeId;\n if (typeof be !== \"number\") return nodes;\n const target = nodes.find((n) => n.backendDOMNodeId === be);\n if (!target) return nodes;\n scopeApplied = true;\n const keep = new Set<string>([target.nodeId]);\n const queue: Protocol.Accessibility.AXNode[] = [target];\n while (queue.length) {\n const cur = queue.shift()!;\n for (const id of cur.childIds ?? []) {\n if (keep.has(id)) continue;\n keep.add(id);\n const child = nodes.find((n) => n.nodeId === id);\n if (child) queue.push(child);\n }\n }\n return nodes\n .filter((n) => keep.has(n.nodeId))\n .map((n) =>\n n.nodeId === target.nodeId ? { ...n, parentId: undefined } : n,\n );\n } catch {\n return nodes;\n }\n })();\n\n const filteredNodes = nodesForOutline.filter((node) => {\n const be = node.backendDOMNodeId;\n return typeof be !== \"number\" || !opts.isIgnoredBackendNode?.(be);\n });\n\n const urlMap: Record<string, string> = {};\n for (const n of filteredNodes) {\n const be = n.backendDOMNodeId;\n if (typeof be !== \"number\") continue;\n const url = extractUrlFromAXNode(n);\n if (!url) continue;\n const enc = opts.encode(be);\n urlMap[enc] = url;\n }\n\n const decorated = decorateRoles(filteredNodes, opts);\n const { tree } = await buildHierarchicalTree(decorated, opts);\n\n const simplified = tree.map((n) => formatTreeLine(n)).join(\"\\n\");\n return { outline: simplified.trimEnd(), urlMap, scopeApplied };\n}\n\nexport function decorateRoles(\n nodes: Protocol.Accessibility.AXNode[],\n opts: A11yOptions,\n): A11yNode[] {\n const asRole = (n: Protocol.Accessibility.AXNode) =>\n String(n.role?.value ?? \"\");\n\n return nodes.map((n) => {\n let encodedId: string | undefined;\n if (typeof n.backendDOMNodeId === \"number\") {\n try {\n encodedId = opts.encode(n.backendDOMNodeId);\n } catch {\n //\n }\n }\n\n let role = asRole(n);\n\n const domIsScrollable = encodedId\n ? opts.scrollableMap[encodedId] === true\n : false;\n const tag = encodedId ? opts.tagNameMap[encodedId] : undefined;\n const isHtmlElement = tag === \"html\";\n if ((domIsScrollable || isHtmlElement) && tag !== \"#document\") {\n const tagLabel = tag && tag.startsWith(\"#\") ? tag.slice(1) : tag;\n role = tagLabel\n ? `scrollable, ${tagLabel}`\n : `scrollable${role ? `, ${role}` : \"\"}`;\n }\n\n // File inputs typically get role \"button\" from Chrome's AX tree;\n // override so they appear as \"input, file\" in the outline.\n if (tag === \"input, file\") {\n role = tag;\n }\n\n return {\n role,\n name: n.name?.value,\n description: n.description?.value,\n value: n.value?.value,\n nodeId: n.nodeId,\n backendDOMNodeId: n.backendDOMNodeId,\n parentId: n.parentId,\n childIds: n.childIds,\n encodedId,\n };\n });\n}\n\nexport async function buildHierarchicalTree(\n nodes: A11yNode[],\n opts: A11yOptions,\n): Promise<{ tree: A11yNode[] }> {\n const nodeMap = new Map<string, A11yNode>();\n\n for (const n of nodes) {\n const keep =\n !!(n.name && n.name.trim()) ||\n !!(n.childIds && n.childIds.length) ||\n !isStructural(n.role);\n if (!keep) continue;\n nodeMap.set(n.nodeId, { ...n });\n }\n\n for (const n of nodes) {\n if (!n.parentId) continue;\n const parent = nodeMap.get(n.parentId);\n const cur = nodeMap.get(n.nodeId);\n if (parent && cur) (parent.children ??= []).push(cur);\n }\n\n const roots = nodes\n .filter((n) => !n.parentId && nodeMap.has(n.nodeId))\n .map((n) => nodeMap.get(n.nodeId)!) as A11yNode[];\n\n const cleaned = (await Promise.all(roots.map(pruneStructuralSafe))).filter(\n Boolean,\n ) as A11yNode[];\n\n return { tree: cleaned };\n\n async function pruneStructuralSafe(node: A11yNode): Promise<A11yNode | null> {\n if (+node.nodeId < 0) return null;\n\n const children = node.children ?? [];\n if (!children.length) {\n return isStructural(node.role) ? null : node;\n }\n\n const cleanedKids = (\n await Promise.all(children.map(pruneStructuralSafe))\n ).filter(Boolean) as A11yNode[];\n\n const prunedStatic = removeRedundantStaticTextChildren(node, cleanedKids);\n\n if (isStructural(node.role)) {\n if (prunedStatic.length === 1) return prunedStatic[0]!;\n if (prunedStatic.length === 0) return null;\n }\n\n let newRole = node.role;\n if ((newRole === \"generic\" || newRole === \"none\") && node.encodedId) {\n const tagName = opts.tagNameMap[node.encodedId];\n if (tagName) newRole = tagName;\n }\n\n if (newRole === \"combobox\" && node.encodedId) {\n const tagName = opts.tagNameMap[node.encodedId];\n if (tagName === \"select\") newRole = \"select\";\n }\n\n return { ...node, role: newRole, children: prunedStatic };\n }\n}\n\nexport function isStructural(role: string): boolean {\n const r = role?.toLowerCase();\n return r === \"generic\" || r === \"none\" || r === \"inlinetextbox\";\n}\n\nexport function extractUrlFromAXNode(\n ax: Protocol.Accessibility.AXNode,\n): string | undefined {\n const props = ax.properties ?? [];\n const urlProp = props.find((p) => p.name === \"url\");\n const value = urlProp?.value?.value;\n return typeof value === \"string\" && value.trim() ? value.trim() : undefined;\n}\n\nexport function removeRedundantStaticTextChildren(\n parent: A11yNode,\n children: A11yNode[],\n): A11yNode[] {\n if (!parent.name) return children;\n const parentNorm = normaliseSpaces(parent.name).trim();\n let combined = \"\";\n for (const c of children) {\n if (c.role === \"StaticText\" && c.name) {\n combined += normaliseSpaces(c.name).trim();\n }\n }\n if (combined === parentNorm) {\n return children.filter((c) => c.role !== \"StaticText\");\n }\n return children;\n}\n"]}
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import { Page } from "../../page.js";
|
|
2
2
|
import type { FrameContext, FrameDomMaps, HybridSnapshot, SnapshotOptions, SessionDomIndex } from "../../../types/private/index.js";
|
|
3
|
+
type IgnoredNodeMap = Map<string, Set<number>>;
|
|
4
|
+
type Interval = {
|
|
5
|
+
start: number;
|
|
6
|
+
end: number;
|
|
7
|
+
};
|
|
8
|
+
type ExclusionIntervalsByFrame = Map<string, Interval[]>;
|
|
3
9
|
/**
|
|
4
10
|
* Capture a hybrid DOM + Accessibility snapshot for the provided page.
|
|
5
11
|
*
|
|
@@ -34,7 +40,7 @@ export declare function buildFrameContext(page: Page): FrameContext;
|
|
|
34
40
|
* Returns `null` when scoping fails (e.g., selector miss) so the caller can
|
|
35
41
|
* fall back to the full multi-frame snapshot.
|
|
36
42
|
*/
|
|
37
|
-
export declare function tryScopedSnapshot(page: Page, options: SnapshotOptions | undefined, context: FrameContext, pierce: boolean): Promise<HybridSnapshot | null>;
|
|
43
|
+
export declare function tryScopedSnapshot(page: Page, options: SnapshotOptions | undefined, context: FrameContext, pierce: boolean, sessionToIndex: Map<string, SessionDomIndex>, exclusionIntervalsByFrame: ExclusionIntervalsByFrame): Promise<HybridSnapshot | null>;
|
|
38
44
|
/**
|
|
39
45
|
* Step 2 – call DOM.getDocument once per unique CDP session and hydrate the
|
|
40
46
|
* result so per-frame slices can share the structure. We key by session id
|
|
@@ -49,13 +55,15 @@ export declare function buildSessionIndexes(page: Page, frames: string[], pierce
|
|
|
49
55
|
* - collects tag/xpath/scrollability maps for DOM-based lookups
|
|
50
56
|
* - fetches its AX tree to produce outlines and URL maps
|
|
51
57
|
*/
|
|
52
|
-
export declare function collectPerFrameMaps(page: Page, context: FrameContext, sessionToIndex: Map<string, SessionDomIndex>, options: SnapshotOptions | undefined, pierce: boolean, frameIds: string[]): Promise<{
|
|
58
|
+
export declare function collectPerFrameMaps(page: Page, context: FrameContext, sessionToIndex: Map<string, SessionDomIndex>, options: SnapshotOptions | undefined, pierce: boolean, frameIds: string[], exclusionIntervalsByFrame: ExclusionIntervalsByFrame): Promise<{
|
|
53
59
|
perFrameMaps: Map<string, FrameDomMaps>;
|
|
54
60
|
perFrameOutlines: Array<{
|
|
55
61
|
frameId: string;
|
|
56
62
|
outline: string;
|
|
57
63
|
}>;
|
|
58
64
|
}>;
|
|
65
|
+
export declare function resolveIgnoredNodes(page: Page, ignoreSelectors: string[] | undefined, context: FrameContext, sessionToIndex: Map<string, SessionDomIndex>): Promise<IgnoredNodeMap>;
|
|
66
|
+
export declare function buildFrameExclusionIntervals(page: Page, context: FrameContext, sessionToIndex: Map<string, SessionDomIndex>, ignoredNodesByFrame: IgnoredNodeMap): Promise<ExclusionIntervalsByFrame>;
|
|
59
67
|
/**
|
|
60
68
|
* Step 4 – walk the frame tree (parent-first) to compute absolute prefixes for
|
|
61
69
|
* every frame. The prefix is the absolute XPath of the iframe element hosting
|
|
@@ -75,3 +83,4 @@ export declare function mergeFramesIntoSnapshot(context: FrameContext, perFrameM
|
|
|
75
83
|
frameId: string;
|
|
76
84
|
outline: string;
|
|
77
85
|
}>, absPrefix: Map<string, string>, iframeHostEncByChild: Map<string, string>, frameIds: string[]): HybridSnapshot;
|
|
86
|
+
export {};
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import { Frame } from "../../frame.js";
|
|
2
|
+
import { FrameSelectorResolver, } from "../../selectorResolver.js";
|
|
1
3
|
import { v3Logger } from "../../../logger.js";
|
|
2
4
|
import { a11yForFrame } from "./a11yTree.js";
|
|
3
|
-
import { resolveCssFocusFrameAndTail, resolveFocusFrameAndTail, } from "./focusSelectors.js";
|
|
5
|
+
import { resolveCssFocusFrameAndTail, resolveFocusFrameAndTail, listChildrenOf, } from "./focusSelectors.js";
|
|
4
6
|
import { buildSessionDomIndex, domMapsForSession, relativizeXPath, } from "./domTree.js";
|
|
5
7
|
import { injectSubtrees } from "./treeFormatUtils.js";
|
|
6
8
|
import { ownerSession, parentSession } from "./sessions.js";
|
|
@@ -26,16 +28,26 @@ import { normalizeXPath, prefixXPath } from "./xpathUtils.js";
|
|
|
26
28
|
export async function captureHybridSnapshot(page, options) {
|
|
27
29
|
const pierce = options?.pierceShadow ?? true;
|
|
28
30
|
const includeIframes = options?.includeIframes !== false;
|
|
31
|
+
const hasIgnoreSelectors = (options?.ignoreSelectors?.length ?? 0) > 0;
|
|
29
32
|
const context = buildFrameContext(page);
|
|
30
|
-
const scopedSnapshot = await tryScopedSnapshot(page, options, context, pierce);
|
|
31
|
-
if (scopedSnapshot)
|
|
32
|
-
return scopedSnapshot;
|
|
33
33
|
const framesInScope = includeIframes ? [...context.frames] : [context.rootId];
|
|
34
34
|
if (!framesInScope.includes(context.rootId)) {
|
|
35
35
|
framesInScope.unshift(context.rootId);
|
|
36
36
|
}
|
|
37
|
+
if (!hasIgnoreSelectors) {
|
|
38
|
+
const scopedSnapshot = await tryScopedSnapshot(page, options, context, pierce, new Map(), new Map());
|
|
39
|
+
if (scopedSnapshot)
|
|
40
|
+
return scopedSnapshot;
|
|
41
|
+
}
|
|
37
42
|
const sessionToIndex = await buildSessionIndexes(page, framesInScope, pierce);
|
|
38
|
-
const
|
|
43
|
+
const ignoredNodesByFrame = await resolveIgnoredNodes(page, options?.ignoreSelectors, context, sessionToIndex);
|
|
44
|
+
const exclusionIntervalsByFrame = await buildFrameExclusionIntervals(page, context, sessionToIndex, ignoredNodesByFrame);
|
|
45
|
+
if (hasIgnoreSelectors) {
|
|
46
|
+
const scopedSnapshot = await tryScopedSnapshot(page, options, context, pierce, sessionToIndex, exclusionIntervalsByFrame);
|
|
47
|
+
if (scopedSnapshot)
|
|
48
|
+
return scopedSnapshot;
|
|
49
|
+
}
|
|
50
|
+
const { perFrameMaps, perFrameOutlines } = await collectPerFrameMaps(page, context, sessionToIndex, options, pierce, framesInScope, exclusionIntervalsByFrame);
|
|
39
51
|
const { absPrefix, iframeHostEncByChild } = await computeFramePrefixes(page, context, perFrameMaps, framesInScope);
|
|
40
52
|
return mergeFramesIntoSnapshot(context, perFrameMaps, perFrameOutlines, absPrefix, iframeHostEncByChild, framesInScope);
|
|
41
53
|
}
|
|
@@ -65,7 +77,7 @@ export function buildFrameContext(page) {
|
|
|
65
77
|
* Returns `null` when scoping fails (e.g., selector miss) so the caller can
|
|
66
78
|
* fall back to the full multi-frame snapshot.
|
|
67
79
|
*/
|
|
68
|
-
export async function tryScopedSnapshot(page, options, context, pierce) {
|
|
80
|
+
export async function tryScopedSnapshot(page, options, context, pierce, sessionToIndex, exclusionIntervalsByFrame) {
|
|
69
81
|
const requestedFocus = options?.focusSelector?.trim();
|
|
70
82
|
if (!requestedFocus)
|
|
71
83
|
return null;
|
|
@@ -106,20 +118,34 @@ export async function tryScopedSnapshot(page, options, context, pierce) {
|
|
|
106
118
|
const { tagNameMap, xpathMap, scrollableMap } = await domMapsForSession(owningSess, targetFrameId, pierce, (fid, be) => `${page.getOrdinal(fid)}-${be}`, sameSessionAsParent);
|
|
107
119
|
const { outline, urlMap, scopeApplied } = await a11yForFrame(owningSess, targetFrameId, {
|
|
108
120
|
focusSelector: tailSelector || undefined,
|
|
121
|
+
isIgnoredBackendNode: makeIsIgnoredBackendNode(targetFrameId, ownerSessionIndexForFrame(page, targetFrameId, sessionToIndex), exclusionIntervalsByFrame),
|
|
109
122
|
tagNameMap,
|
|
110
123
|
experimental: options?.experimental ?? false,
|
|
111
124
|
scrollableMap,
|
|
112
125
|
encode: (backendNodeId) => `${page.getOrdinal(targetFrameId)}-${backendNodeId}`,
|
|
113
126
|
});
|
|
114
127
|
const scopedXpathMap = {};
|
|
128
|
+
const isIgnoredBackendNode = makeIsIgnoredBackendNode(targetFrameId, ownerSessionIndexForFrame(page, targetFrameId, sessionToIndex), exclusionIntervalsByFrame);
|
|
115
129
|
const abs = absPrefix ?? "";
|
|
116
130
|
const isRoot = !abs || abs === "/";
|
|
117
131
|
if (isRoot) {
|
|
118
|
-
Object.
|
|
132
|
+
for (const [encId, xp] of Object.entries(xpathMap)) {
|
|
133
|
+
const backendNodeId = parseEncodedBackendNodeId(encId);
|
|
134
|
+
if (typeof backendNodeId === "number" &&
|
|
135
|
+
isIgnoredBackendNode?.(backendNodeId)) {
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
scopedXpathMap[encId] = xp;
|
|
139
|
+
}
|
|
119
140
|
}
|
|
120
141
|
else {
|
|
121
142
|
// Prefix relative XPaths so the scoped result matches the global encoding.
|
|
122
143
|
for (const [encId, xp] of Object.entries(xpathMap)) {
|
|
144
|
+
const backendNodeId = parseEncodedBackendNodeId(encId);
|
|
145
|
+
if (typeof backendNodeId === "number" &&
|
|
146
|
+
isIgnoredBackendNode?.(backendNodeId)) {
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
123
149
|
scopedXpathMap[encId] = prefixXPath(abs, xp);
|
|
124
150
|
}
|
|
125
151
|
}
|
|
@@ -175,7 +201,7 @@ export async function buildSessionIndexes(page, frames, pierce) {
|
|
|
175
201
|
* - collects tag/xpath/scrollability maps for DOM-based lookups
|
|
176
202
|
* - fetches its AX tree to produce outlines and URL maps
|
|
177
203
|
*/
|
|
178
|
-
export async function collectPerFrameMaps(page, context, sessionToIndex, options, pierce, frameIds) {
|
|
204
|
+
export async function collectPerFrameMaps(page, context, sessionToIndex, options, pierce, frameIds, exclusionIntervalsByFrame) {
|
|
179
205
|
const perFrameMaps = new Map();
|
|
180
206
|
const perFrameOutlines = [];
|
|
181
207
|
for (const frameId of frameIds) {
|
|
@@ -188,29 +214,19 @@ export async function collectPerFrameMaps(page, context, sessionToIndex, options
|
|
|
188
214
|
}
|
|
189
215
|
const parentId = context.parentByFrame.get(frameId);
|
|
190
216
|
const sameSessionAsParent = !!parentId && ownerSession(page, parentId) === sess;
|
|
191
|
-
|
|
192
|
-
if (sameSessionAsParent) {
|
|
193
|
-
try {
|
|
194
|
-
const { backendNodeId } = await sess.send("DOM.getFrameOwner", { frameId });
|
|
195
|
-
if (typeof backendNodeId === "number") {
|
|
196
|
-
const cdBe = idx.contentDocRootByIframe.get(backendNodeId);
|
|
197
|
-
if (typeof cdBe === "number")
|
|
198
|
-
docRootBe = cdBe;
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
catch {
|
|
202
|
-
//
|
|
203
|
-
}
|
|
204
|
-
}
|
|
217
|
+
const docRootBe = await resolveFrameDocRootBackendId(page, frameId, idx, sameSessionAsParent);
|
|
205
218
|
const tagNameMap = {};
|
|
206
219
|
const xpathMap = {};
|
|
207
220
|
const scrollableMap = {};
|
|
221
|
+
const isIgnoredBackendNode = makeIsIgnoredBackendNode(frameId, idx, exclusionIntervalsByFrame);
|
|
208
222
|
const enc = (be) => `${page.getOrdinal(frameId)}-${be}`;
|
|
209
223
|
const baseAbs = idx.absByBe.get(docRootBe) ?? "/";
|
|
210
224
|
for (const [be, nodeAbs] of idx.absByBe.entries()) {
|
|
211
225
|
const nodeDocRoot = idx.docRootOf.get(be);
|
|
212
226
|
if (nodeDocRoot !== docRootBe)
|
|
213
227
|
continue;
|
|
228
|
+
if (isIgnoredBackendNode?.(be))
|
|
229
|
+
continue;
|
|
214
230
|
// Translate absolute XPaths into document-relative ones for this frame.
|
|
215
231
|
const rel = relativizeXPath(baseAbs, nodeAbs);
|
|
216
232
|
const key = enc(be);
|
|
@@ -222,6 +238,7 @@ export async function collectPerFrameMaps(page, context, sessionToIndex, options
|
|
|
222
238
|
scrollableMap[key] = true;
|
|
223
239
|
}
|
|
224
240
|
const { outline, urlMap } = await a11yForFrame(sess, frameId, {
|
|
241
|
+
isIgnoredBackendNode,
|
|
225
242
|
experimental: options?.experimental ?? false,
|
|
226
243
|
tagNameMap,
|
|
227
244
|
scrollableMap,
|
|
@@ -232,6 +249,234 @@ export async function collectPerFrameMaps(page, context, sessionToIndex, options
|
|
|
232
249
|
}
|
|
233
250
|
return { perFrameMaps, perFrameOutlines };
|
|
234
251
|
}
|
|
252
|
+
export async function resolveIgnoredNodes(page, ignoreSelectors, context, sessionToIndex) {
|
|
253
|
+
const ignoredNodesByFrame = new Map();
|
|
254
|
+
for (const rawSelector of ignoreSelectors ?? []) {
|
|
255
|
+
const selector = rawSelector.trim();
|
|
256
|
+
if (!selector)
|
|
257
|
+
continue;
|
|
258
|
+
try {
|
|
259
|
+
const resolved = await resolveIgnoredNodesForSelector(page, selector, context, sessionToIndex);
|
|
260
|
+
for (const match of resolved) {
|
|
261
|
+
const nodes = ignoredNodesByFrame.get(match.frameId) ?? new Set();
|
|
262
|
+
nodes.add(match.backendNodeId);
|
|
263
|
+
ignoredNodesByFrame.set(match.frameId, nodes);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
catch {
|
|
267
|
+
continue;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
return ignoredNodesByFrame;
|
|
271
|
+
}
|
|
272
|
+
async function resolveIgnoredNodesForSelector(page, selector, context, sessionToIndex) {
|
|
273
|
+
const looksLikeXPath = /^xpath=/i.test(selector) || selector.startsWith("/");
|
|
274
|
+
if (looksLikeXPath) {
|
|
275
|
+
const hit = await resolveFocusFrameAndTail(page, normalizeXPath(selector), context.parentByFrame, context.rootId);
|
|
276
|
+
const targetFrameId = hit.targetFrameId;
|
|
277
|
+
const tailXPath = hit.tailXPath || "/";
|
|
278
|
+
if (tailXPath === "/") {
|
|
279
|
+
const idx = ownerSessionIndexForFrame(page, targetFrameId, sessionToIndex);
|
|
280
|
+
if (!idx)
|
|
281
|
+
return [];
|
|
282
|
+
const parentId = context.parentByFrame.get(targetFrameId);
|
|
283
|
+
const sameSessionAsParent = !!parentId &&
|
|
284
|
+
ownerSession(page, parentId) === ownerSession(page, targetFrameId);
|
|
285
|
+
const backendNodeId = await resolveFrameDocRootBackendId(page, targetFrameId, idx, sameSessionAsParent);
|
|
286
|
+
return [{ frameId: targetFrameId, backendNodeId }];
|
|
287
|
+
}
|
|
288
|
+
return resolveIgnoredNodesInFrame(page, targetFrameId, {
|
|
289
|
+
kind: "xpath",
|
|
290
|
+
value: tailXPath,
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
const hit = await resolveCssFocusFrameAndTail(page, selector, context.parentByFrame, context.rootId);
|
|
294
|
+
const targetFrameId = hit.targetFrameId;
|
|
295
|
+
return resolveIgnoredNodesInFrame(page, targetFrameId, {
|
|
296
|
+
kind: "css",
|
|
297
|
+
value: hit.tailSelector || selector,
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
async function resolveIgnoredNodesInFrame(page, frameId, query) {
|
|
301
|
+
const session = ownerSession(page, frameId);
|
|
302
|
+
const frame = new Frame(session, frameId, "", false);
|
|
303
|
+
const resolver = new FrameSelectorResolver(frame);
|
|
304
|
+
const resolvedNodes = await resolver.resolveAll(query);
|
|
305
|
+
if (!resolvedNodes.length)
|
|
306
|
+
return [];
|
|
307
|
+
const backendNodeIds = await describeResolvedNodes(session, resolvedNodes);
|
|
308
|
+
return backendNodeIds.map((backendNodeId) => ({ frameId, backendNodeId }));
|
|
309
|
+
}
|
|
310
|
+
async function describeResolvedNodes(session, resolvedNodes) {
|
|
311
|
+
const backendNodeIds = new Set();
|
|
312
|
+
try {
|
|
313
|
+
for (const resolvedNode of resolvedNodes) {
|
|
314
|
+
const desc = await session.send("DOM.describeNode", { objectId: resolvedNode.objectId });
|
|
315
|
+
const backendNodeId = desc.node.backendNodeId;
|
|
316
|
+
if (typeof backendNodeId === "number") {
|
|
317
|
+
backendNodeIds.add(backendNodeId);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
finally {
|
|
322
|
+
await Promise.all(resolvedNodes.map((resolvedNode) => session
|
|
323
|
+
.send("Runtime.releaseObject", { objectId: resolvedNode.objectId })
|
|
324
|
+
.catch(() => { })));
|
|
325
|
+
}
|
|
326
|
+
return [...backendNodeIds];
|
|
327
|
+
}
|
|
328
|
+
export async function buildFrameExclusionIntervals(page, context, sessionToIndex, ignoredNodesByFrame) {
|
|
329
|
+
const intervalsByFrame = new Map();
|
|
330
|
+
if (!ignoredNodesByFrame.size)
|
|
331
|
+
return intervalsByFrame;
|
|
332
|
+
const childFramesByParent = await resolveChildFramesByParent(page, context);
|
|
333
|
+
const excludedFrames = new Set();
|
|
334
|
+
const pushInterval = (frameId, start, end) => {
|
|
335
|
+
const intervals = intervalsByFrame.get(frameId) ?? [];
|
|
336
|
+
intervals.push({ start, end });
|
|
337
|
+
intervalsByFrame.set(frameId, intervals);
|
|
338
|
+
};
|
|
339
|
+
const excludeFrameSubtree = async (frameId) => {
|
|
340
|
+
if (excludedFrames.has(frameId))
|
|
341
|
+
return;
|
|
342
|
+
excludedFrames.add(frameId);
|
|
343
|
+
const idx = ownerSessionIndexForFrame(page, frameId, sessionToIndex);
|
|
344
|
+
if (!idx)
|
|
345
|
+
return;
|
|
346
|
+
const parentId = context.parentByFrame.get(frameId);
|
|
347
|
+
const sameSessionAsParent = !!parentId &&
|
|
348
|
+
ownerSession(page, parentId) === ownerSession(page, frameId);
|
|
349
|
+
const docRootBe = await resolveFrameDocRootBackendId(page, frameId, idx, sameSessionAsParent);
|
|
350
|
+
const start = idx.enterByBe.get(docRootBe);
|
|
351
|
+
const end = idx.exitByBe.get(docRootBe);
|
|
352
|
+
if (typeof start === "number" && typeof end === "number") {
|
|
353
|
+
pushInterval(frameId, start, end);
|
|
354
|
+
}
|
|
355
|
+
for (const childFrameId of listChildrenOf(context.parentByFrame, frameId)) {
|
|
356
|
+
await excludeFrameSubtree(childFrameId);
|
|
357
|
+
}
|
|
358
|
+
};
|
|
359
|
+
const excludeIgnoredNode = async (frameId, backendNodeId) => {
|
|
360
|
+
const idx = ownerSessionIndexForFrame(page, frameId, sessionToIndex);
|
|
361
|
+
if (!idx)
|
|
362
|
+
return;
|
|
363
|
+
const start = idx.enterByBe.get(backendNodeId);
|
|
364
|
+
const end = idx.exitByBe.get(backendNodeId);
|
|
365
|
+
if (typeof start !== "number" || typeof end !== "number")
|
|
366
|
+
return;
|
|
367
|
+
pushInterval(frameId, start, end);
|
|
368
|
+
for (const childFrame of childFramesByParent.get(frameId) ?? []) {
|
|
369
|
+
const hostEnter = idx.enterByBe.get(childFrame.hostBackendNodeId);
|
|
370
|
+
if (typeof hostEnter !== "number")
|
|
371
|
+
continue;
|
|
372
|
+
if (hostEnter < start || hostEnter > end)
|
|
373
|
+
continue;
|
|
374
|
+
await excludeFrameSubtree(childFrame.childFrameId);
|
|
375
|
+
}
|
|
376
|
+
};
|
|
377
|
+
for (const [frameId, backendNodeIds] of ignoredNodesByFrame.entries()) {
|
|
378
|
+
for (const backendNodeId of backendNodeIds) {
|
|
379
|
+
await excludeIgnoredNode(frameId, backendNodeId);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
for (const [frameId, intervals] of intervalsByFrame.entries()) {
|
|
383
|
+
intervals.sort((a, b) => a.start - b.start || a.end - b.end);
|
|
384
|
+
const merged = [];
|
|
385
|
+
for (const interval of intervals) {
|
|
386
|
+
const prev = merged[merged.length - 1];
|
|
387
|
+
if (!prev || interval.start > prev.end) {
|
|
388
|
+
merged.push({ ...interval });
|
|
389
|
+
continue;
|
|
390
|
+
}
|
|
391
|
+
if (interval.end > prev.end)
|
|
392
|
+
prev.end = interval.end;
|
|
393
|
+
}
|
|
394
|
+
intervalsByFrame.set(frameId, merged);
|
|
395
|
+
}
|
|
396
|
+
return intervalsByFrame;
|
|
397
|
+
}
|
|
398
|
+
async function resolveChildFramesByParent(page, context) {
|
|
399
|
+
const childFramesByParent = new Map();
|
|
400
|
+
for (const frameId of context.frames) {
|
|
401
|
+
const parentId = context.parentByFrame.get(frameId);
|
|
402
|
+
if (!parentId)
|
|
403
|
+
continue;
|
|
404
|
+
const session = parentSession(page, context.parentByFrame, frameId);
|
|
405
|
+
if (!session)
|
|
406
|
+
continue;
|
|
407
|
+
try {
|
|
408
|
+
const { backendNodeId } = await session.send("DOM.getFrameOwner", { frameId });
|
|
409
|
+
if (typeof backendNodeId !== "number")
|
|
410
|
+
continue;
|
|
411
|
+
const childFrames = childFramesByParent.get(parentId) ?? [];
|
|
412
|
+
childFrames.push({
|
|
413
|
+
childFrameId: frameId,
|
|
414
|
+
hostBackendNodeId: backendNodeId,
|
|
415
|
+
});
|
|
416
|
+
childFramesByParent.set(parentId, childFrames);
|
|
417
|
+
}
|
|
418
|
+
catch {
|
|
419
|
+
continue;
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
return childFramesByParent;
|
|
423
|
+
}
|
|
424
|
+
function makeIsIgnoredBackendNode(frameId, idx, exclusionIntervalsByFrame) {
|
|
425
|
+
if (!idx)
|
|
426
|
+
return undefined;
|
|
427
|
+
const intervals = exclusionIntervalsByFrame.get(frameId);
|
|
428
|
+
if (!intervals?.length)
|
|
429
|
+
return undefined;
|
|
430
|
+
return (backendNodeId) => {
|
|
431
|
+
const enter = idx.enterByBe.get(backendNodeId);
|
|
432
|
+
if (typeof enter !== "number")
|
|
433
|
+
return false;
|
|
434
|
+
let lo = 0;
|
|
435
|
+
let hi = intervals.length - 1;
|
|
436
|
+
while (lo <= hi) {
|
|
437
|
+
const mid = (lo + hi) >> 1;
|
|
438
|
+
const interval = intervals[mid];
|
|
439
|
+
if (enter < interval.start) {
|
|
440
|
+
hi = mid - 1;
|
|
441
|
+
}
|
|
442
|
+
else if (enter > interval.end) {
|
|
443
|
+
lo = mid + 1;
|
|
444
|
+
}
|
|
445
|
+
else {
|
|
446
|
+
return true;
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
return false;
|
|
450
|
+
};
|
|
451
|
+
}
|
|
452
|
+
function parseEncodedBackendNodeId(encodedId) {
|
|
453
|
+
const parts = encodedId.split("-");
|
|
454
|
+
if (parts.length !== 2)
|
|
455
|
+
return undefined;
|
|
456
|
+
const backendNodeId = Number(parts[1]);
|
|
457
|
+
return Number.isFinite(backendNodeId) ? backendNodeId : undefined;
|
|
458
|
+
}
|
|
459
|
+
function ownerSessionIndexForFrame(page, frameId, sessionToIndex) {
|
|
460
|
+
const session = ownerSession(page, frameId);
|
|
461
|
+
return sessionToIndex.get(session.id ?? "root");
|
|
462
|
+
}
|
|
463
|
+
async function resolveFrameDocRootBackendId(page, frameId, idx, sameSessionAsParent) {
|
|
464
|
+
if (!sameSessionAsParent)
|
|
465
|
+
return idx.rootBackend;
|
|
466
|
+
const session = ownerSession(page, frameId);
|
|
467
|
+
try {
|
|
468
|
+
const { backendNodeId } = await session.send("DOM.getFrameOwner", { frameId });
|
|
469
|
+
if (typeof backendNodeId === "number") {
|
|
470
|
+
const docRootBe = idx.contentDocRootByIframe.get(backendNodeId);
|
|
471
|
+
if (typeof docRootBe === "number")
|
|
472
|
+
return docRootBe;
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
catch {
|
|
476
|
+
//
|
|
477
|
+
}
|
|
478
|
+
return idx.rootBackend;
|
|
479
|
+
}
|
|
235
480
|
/**
|
|
236
481
|
* Step 4 – walk the frame tree (parent-first) to compute absolute prefixes for
|
|
237
482
|
* every frame. The prefix is the absolute XPath of the iframe element hosting
|