| 1 | import type { Params } from "../params.js"; |
| 2 | import { str } from "../params.js"; |
| 3 | import { browserFailure, staleReference } from "./errors.js"; |
| 4 | import type { DocumentRegistry } from "./documents.js"; |
| 5 | import type { BrowserTab } from "./surfaceManager.js"; |
| 6 | import { frameForRef } from "./refResolver.js"; |
| 7 | import { scriptCall } from "./pageScripts.js"; |
| 8 | import { REGISTRY_KEY, runInFrame } from "./snapshot.js"; |
| 9 | |
| 10 | export async function queryElements(tab: BrowserTab, params: Params, documents: DocumentRegistry) { |
| 11 | const documentToken = str(params, "documentToken"); |
| 12 | const binding = documents.lookup(documentToken); |
| 13 | if (!binding || binding.tabId !== tab.id || binding.epoch !== tab.epoch) throw staleReference("query needs the current documentToken"); |
| 14 | const containerRef = str(params, "containerRef"); |
| 15 | const frameRef = str(params, "frameRef"); |
| 16 | const target = containerRef || frameRef ? frameForRef(tab.view.page, binding, containerRef || frameRef) : { frame: tab.view.page.mainFrame, binding: binding.frames[0] }; |
| 17 | if (containerRef && frameRef && frameForRef(tab.view.page, binding, frameRef).binding !== target.binding) throw staleReference("container and frame reference belong to different frames"); |
| 18 | if (!target.binding) throw staleReference("snapshot has no frame"); |
| 19 | const criteria = Object.fromEntries(["role", "name", "text", "testId", "state"].filter(key => typeof params[key] === "string").map(key => [key, params[key]])); |
| 20 | if (criteria.state && !["visible", "hidden", "enabled", "disabled", "checked", "editable"].includes(String(criteria.state))) throw new Error("invalid element state"); |
| 21 | const raw = await runInFrame(tab.view.page, target.frame, scriptCall("pageQuery", { ...criteria, key: REGISTRY_KEY, snapshotId: binding.snapshotId, docId: target.binding.docId, prefix: target.binding.prefix, containerRef })); |
| 22 | const result = raw as { count?: number; refs?: string[]; ambiguous?: boolean; state?: boolean; error?: string }; |
| 23 | if (result?.error === "stale_document") throw staleReference("query document changed"); |
| 24 | if (!Number.isInteger(result?.count) || !Array.isArray(result?.refs)) throw browserFailure("script_runtime_error", "query returned an invalid result"); |
| 25 | return { ...result, documentToken, url: tab.view.page.getURL() }; |
| 26 | } |
| 27 |