injectedScriptSource.js 216 KB

12345678
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", {
  3. value: true
  4. });
  5. exports.source = void 0;
  6. const source = "\nvar __export = (target, all) => {for (var name in all) target[name] = all[name];};\nvar __toCommonJS = mod => ({ ...mod, __esModule: true });\n// packages/playwright-core/src/server/injected/injectedScript.ts\nvar injectedScript_exports = {};\n__export(injectedScript_exports, {\n InjectedScript: () => InjectedScript\n});\nmodule.exports = __toCommonJS(injectedScript_exports);\n\n// packages/playwright-core/src/server/injected/xpathSelectorEngine.ts\nvar XPathEngine = {\n queryAll(root, selector) {\n if (selector.startsWith(\"/\"))\n selector = \".\" + selector;\n const result = [];\n const document = root.ownerDocument || root;\n if (!document)\n return result;\n const it = document.evaluate(selector, root, null, XPathResult.ORDERED_NODE_ITERATOR_TYPE);\n for (let node = it.iterateNext(); node; node = it.iterateNext()) {\n if (node.nodeType === Node.ELEMENT_NODE)\n result.push(node);\n }\n return result;\n }\n};\n\n// packages/playwright-core/src/server/injected/domUtils.ts\nfunction isInsideScope(scope, element) {\n while (element) {\n if (scope.contains(element))\n return true;\n element = enclosingShadowHost(element);\n }\n return false;\n}\nfunction parentElementOrShadowHost(element) {\n if (element.parentElement)\n return element.parentElement;\n if (!element.parentNode)\n return;\n if (element.parentNode.nodeType === 11 && element.parentNode.host)\n return element.parentNode.host;\n}\nfunction enclosingShadowRootOrDocument(element) {\n let node = element;\n while (node.parentNode)\n node = node.parentNode;\n if (node.nodeType === 11 || node.nodeType === 9)\n return node;\n}\nfunction enclosingShadowHost(element) {\n while (element.parentElement)\n element = element.parentElement;\n return parentElementOrShadowHost(element);\n}\nfunction closestCrossShadow(element, css, scope) {\n while (element) {\n const closest = element.closest(css);\n if (scope && closest !== scope && (closest == null ? void 0 : closest.contains(scope)))\n return;\n if (closest)\n return closest;\n element = enclosingShadowHost(element);\n }\n}\nfunction getElementComputedStyle(element, pseudo) {\n return element.ownerDocument && element.ownerDocument.defaultView ? element.ownerDocument.defaultView.getComputedStyle(element, pseudo) : void 0;\n}\nfunction isElementStyleVisibilityVisible(element, style) {\n style = style != null ? style : getElementComputedStyle(element);\n if (!style)\n return true;\n if (Element.prototype.checkVisibility) {\n if (!element.checkVisibility({ checkOpacity: false, checkVisibilityCSS: false }))\n return false;\n } else {\n const detailsOrSummary = element.closest(\"details,summary\");\n if (detailsOrSummary !== element && (detailsOrSummary == null ? void 0 : detailsOrSummary.nodeName) === \"DETAILS\" && !detailsOrSummary.open)\n return false;\n }\n if (style.visibility !== \"visible\")\n return false;\n return true;\n}\nfunction isElementVisible(element) {\n const style = getElementComputedStyle(element);\n if (!style)\n return true;\n if (style.display === \"contents\") {\n for (let child = element.firstChild; child; child = child.nextSibling) {\n if (child.nodeType === 1 && isElementVisible(child))\n return true;\n if (child.nodeType === 3 && isVisibleTextNode(child))\n return true;\n }\n return false;\n }\n if (!isElementStyleVisibilityVisible(element, style))\n return false;\n const rect = element.getBoundingClientRect();\n return rect.width > 0 && rect.height > 0;\n}\nfunction isVisibleTextNode(node) {\n const range = node.ownerDocument.createRange();\n range.selectNode(node);\n const rect = range.getBoundingClientRect();\n return rect.width > 0 && rect.height > 0;\n}\n\n// packages/playwright-core/src/server/injected/roleUtils.ts\nfunction hasExplicitAccessibleName(e) {\n return e.hasAttribute(\"aria-label\") || e.hasAttribute(\"aria-labelledby\");\n}\nvar kAncestorPreventingLandmark = \"article:not([role]), aside:not([role]), main:not([role]), nav:not([role]), section:not([role]), [role=article], [role=complementary], [role=main], [role=navigation], [role=region]\";\nvar kGlobalAriaAttributes = [\n \"aria-atomic\",\n \"aria-busy\",\n \"aria-controls\",\n \"aria-current\",\n \"aria-describedby\",\n \"aria-details\",\n \"aria-disabled\",\n \"aria-dropeffect\",\n \"aria-errormessage\",\n \"aria-flowto\",\n \"aria-grabbed\",\n \"aria-haspopup\",\n \"aria-hidden\",\n \"aria-invalid\",\n \"aria-keyshortcuts\",\n \"aria-label\",\n \"aria-labelledby\",\n \"aria-live\",\n \"aria-owns\",\n \"aria-relevant\",\n \"aria-roledescription\"\n];\nfunction hasGlobalAriaAttribute(e) {\n return kGlobalAriaAttributes.some((a) => e.hasAttribute(a));\n}\nvar kImplicitRoleByTagName = {\n \"A\": (e) => {\n return e.hasAttribute(\"href\") ? \"link\" : null;\n },\n \"AREA\": (e) => {\n return e.hasAttribute(\"href\") ? \"link\" : null;\n },\n \"ARTICLE\": () => \"article\",\n \"ASIDE\": () => \"complementary\",\n \"BLOCKQUOTE\": () => \"blockquote\",\n \"BUTTON\": () => \"button\",\n \"CAPTION\": () => \"caption\",\n \"CODE\": () => \"code\",\n \"DATALIST\": () => \"listbox\",\n \"DD\": () => \"definition\",\n \"DEL\": () => \"deletion\",\n \"DETAILS\": () => \"group\",\n \"DFN\": () => \"term\",\n \"DIALOG\": () => \"dialog\",\n \"DT\": () => \"term\",\n \"EM\": () => \"emphasis\",\n \"FIELDSET\": () => \"group\",\n \"FIGURE\": () => \"figure\",\n \"FOOTER\": (e) => closestCrossShadow(e, kAncestorPreventingLandmark) ? null : \"contentinfo\",\n \"FORM\": (e) => hasExplicitAccessibleName(e) ? \"form\" : null,\n \"H1\": () => \"heading\",\n \"H2\": () => \"heading\",\n \"H3\": () => \"heading\",\n \"H4\": () => \"heading\",\n \"H5\": () => \"heading\",\n \"H6\": () => \"heading\",\n \"HEADER\": (e) => closestCrossShadow(e, kAncestorPreventingLandmark) ? null : \"banner\",\n \"HR\": () => \"separator\",\n \"HTML\": () => \"document\",\n \"IMG\": (e) => e.getAttribute(\"alt\") === \"\" && !hasGlobalAriaAttribute(e) && Number.isNaN(Number(String(e.getAttribute(\"tabindex\")))) ? \"presentation\" : \"img\",\n \"INPUT\": (e) => {\n const type = e.type.toLowerCase();\n if (type === \"search\")\n return e.hasAttribute(\"list\") ? \"combobox\" : \"searchbox\";\n if ([\"email\", \"tel\", \"text\", \"url\", \"\"].includes(type)) {\n const list = getIdRefs(e, e.getAttribute(\"list\"))[0];\n return list && list.tagName === \"DATALIST\" ? \"combobox\" : \"textbox\";\n }\n if (type === \"hidden\")\n return \"\";\n return {\n \"button\": \"button\",\n \"checkbox\": \"checkbox\",\n \"image\": \"button\",\n \"number\": \"spinbutton\",\n \"radio\": \"radio\",\n \"range\": \"slider\",\n \"reset\": \"button\",\n \"submit\": \"button\"\n }[type] || \"textbox\";\n },\n \"INS\": () => \"insertion\",\n \"LI\": () => \"listitem\",\n \"MAIN\": () => \"main\",\n \"MARK\": () => \"mark\",\n \"MATH\": () => \"math\",\n \"MENU\": () => \"list\",\n \"METER\": () => \"meter\",\n \"NAV\": () => \"navigation\",\n \"OL\": () => \"list\",\n \"OPTGROUP\": () => \"group\",\n \"OPTION\": () => \"option\",\n \"OUTPUT\": () => \"status\",\n \"P\": () => \"paragraph\",\n \"PROGRESS\": () => \"progressbar\",\n \"SECTION\": (e) => hasExplicitAccessibleName(e) ? \"region\" : null,\n \"SELECT\": (e) => e.hasAttribute(\"multiple\") || e.size > 1 ? \"listbox\" : \"combobox\",\n \"STRONG\": () => \"strong\",\n \"SUB\": () => \"subscript\",\n \"SUP\": () => \"superscript\",\n \"SVG\": () => \"img\",\n \"TABLE\": () => \"table\",\n \"TBODY\": () => \"rowgroup\",\n \"TD\": (e) => {\n const table = closestCrossShadow(e, \"table\");\n const role = table ? getExplicitAriaRole(table) : \"\";\n return role === \"grid\" || role === \"treegrid\" ? \"gridcell\" : \"cell\";\n },\n \"TEXTAREA\": () => \"textbox\",\n \"TFOOT\": () => \"rowgroup\",\n \"TH\": (e) => {\n if (e.getAttribute(\"scope\") === \"col\")\n return \"columnheader\";\n if (e.getAttribute(\"scope\") === \"row\")\n return \"rowheader\";\n const table = closestCrossShadow(e, \"table\");\n const role = table ? getExplicitAriaRole(table) : \"\";\n return role === \"grid\" || role === \"treegrid\" ? \"gridcell\" : \"cell\";\n },\n \"THEAD\": () => \"rowgroup\",\n \"TIME\": () => \"time\",\n \"TR\": () => \"row\",\n \"UL\": () => \"list\"\n};\nvar kPresentationInheritanceParents = {\n \"DD\": [\"DL\", \"DIV\"],\n \"DIV\": [\"DL\"],\n \"DT\": [\"DL\", \"DIV\"],\n \"LI\": [\"OL\", \"UL\"],\n \"TBODY\": [\"TABLE\"],\n \"TD\": [\"TR\"],\n \"TFOOT\": [\"TABLE\"],\n \"TH\": [\"TR\"],\n \"THEAD\": [\"TABLE\"],\n \"TR\": [\"THEAD\", \"TBODY\", \"TFOOT\", \"TABLE\"]\n};\nfunction getImplicitAriaRole(element) {\n var _a;\n const implicitRole = ((_a = kImplicitRoleByTagName[element.tagName.toUpperCase()]) == null ? void 0 : _a.call(kImplicitRoleByTagName, element)) || \"\";\n if (!implicitRole)\n return null;\n let ancestor = element;\n while (ancestor) {\n const parent = parentElementOrShadowHost(ancestor);\n const parents = kPresentationInheritanceParents[ancestor.tagName];\n if (!parents || !parent || !parents.includes(parent.tagName))\n break;\n const parentExplicitRole = getExplicitAriaRole(parent);\n if ((parentExplicitRole === \"none\" || parentExplicitRole === \"presentation\") && !hasPresentationConflictResolution(parent))\n return parentExplicitRole;\n ancestor = parent;\n }\n return implicitRole;\n}\nvar allRoles = [\n \"alert\",\n \"alertdialog\",\n \"application\",\n \"article\",\n \"banner\",\n \"blockquote\",\n \"button\",\n \"caption\",\n \"cell\",\n \"checkbox\",\n \"code\",\n \"columnheader\",\n \"combobox\",\n \"command\",\n \"complementary\",\n \"composite\",\n \"contentinfo\",\n \"definition\",\n \"deletion\",\n \"dialog\",\n \"directory\",\n \"document\",\n \"emphasis\",\n \"feed\",\n \"figure\",\n \"form\",\n \"generic\",\n \"grid\",\n \"gridcell\",\n \"group\",\n \"heading\",\n \"img\",\n \"input\",\n \"insertion\",\n \"landmark\",\n \"link\",\n \"list\",\n \"listbox\",\n \"listitem\",\n \"log\",\n \"main\",\n \"marquee\",\n \"math\",\n \"meter\",\n \"menu\",\n \"menubar\",\n \"menuitem\",\n \"menuitemcheckbox\",\n \"menuitemradio\",\n \"navigation\",\n \"none\",\n \"note\",\n \"option\",\n \"paragraph\",\n \"presentation\",\n \"progressbar\",\n \"radio\",\n \"radiogroup\",\n \"range\",\n \"region\",\n \"roletype\",\n \"row\",\n \"rowgroup\",\n \"rowheader\",\n \"scrollbar\",\n \"search\",\n \"searchbox\",\n \"section\",\n \"sectionhead\",\n \"select\",\n \"separator\",\n \"slider\",\n \"spinbutton\",\n \"status\",\n \"strong\",\n \"structure\",\n \"subscript\",\n \"superscript\",\n \"switch\",\n \"tab\",\n \"table\",\n \"tablist\",\n \"tabpanel\",\n \"term\",\n \"textbox\",\n \"time\",\n \"timer\",\n \"toolbar\",\n \"tooltip\",\n \"tree\",\n \"treegrid\",\n \"treeitem\",\n \"widget\",\n \"window\"\n];\nvar abstractRoles = [\"command\", \"composite\", \"input\", \"landmark\", \"range\", \"roletype\", \"section\", \"sectionhead\", \"select\", \"structure\", \"widget\", \"window\"];\nvar validRoles = allRoles.filter((role) => !abstractRoles.includes(role));\nfunction getExplicitAriaRole(element) {\n const roles = (element.getAttribute(\"role\") || \"\").split(\" \").map((role) => role.trim());\n return roles.find((role) => validRoles.includes(role)) || null;\n}\nfunction hasPresentationConflictResolution(element) {\n return !hasGlobalAriaAttribute(element);\n}\nfunction getAriaRole(element) {\n const explicitRole = getExplicitAriaRole(element);\n if (!explicitRole)\n return getImplicitAriaRole(element);\n if ((explicitRole === \"none\" || explicitRole === \"presentation\") && hasPresentationConflictResolution(element))\n return getImplicitAriaRole(element);\n return explicitRole;\n}\nfunction getAriaBoolean(attr) {\n return attr === null ? void 0 : attr.toLowerCase() === \"true\";\n}\nfunction isElementHiddenForAria(element) {\n if ([\"STYLE\", \"SCRIPT\", \"NOSCRIPT\", \"TEMPLATE\"].includes(element.tagName))\n return true;\n const style = getElementComputedStyle(element);\n const isSlot = element.nodeName === \"SLOT\";\n if ((style == null ? void 0 : style.display) === \"contents\" && !isSlot) {\n for (let child = element.firstChild; child; child = child.nextSibling) {\n if (child.nodeType === 1 && !isElementHiddenForAria(child))\n return false;\n if (child.nodeType === 3 && isVisibleTextNode(child))\n return false;\n }\n return true;\n }\n const isOptionInsideSelect = element.nodeName === \"OPTION\" && !!element.closest(\"select\");\n if (!isOptionInsideSelect && !isSlot && !isElementStyleVisibilityVisible(element, style))\n return true;\n return belongsToDisplayNoneOrAriaHiddenOrNonSlotted(element);\n}\nfunction belongsToDisplayNoneOrAriaHiddenOrNonSlotted(element) {\n let hidden = cacheIsHidden == null ? void 0 : cacheIsHidden.get(element);\n if (hidden === void 0) {\n hidden = false;\n if (element.parentElement && element.parentElement.shadowRoot && !element.assignedSlot)\n hidden = true;\n if (!hidden) {\n const style = getElementComputedStyle(element);\n hidden = !style || style.display === \"none\" || getAriaBoolean(element.getAttribute(\"aria-hidden\")) === true;\n }\n if (!hidden) {\n const parent = parentElementOrShadowHost(element);\n if (parent)\n hidden = belongsToDisplayNoneOrAriaHiddenOrNonSlotted(parent);\n }\n cacheIsHidden == null ? void 0 : cacheIsHidden.set(element, hidden);\n }\n return hidden;\n}\nfunction getIdRefs(element, ref) {\n if (!ref)\n return [];\n const root = enclosingShadowRootOrDocument(element);\n if (!root)\n return [];\n try {\n const ids = ref.split(\" \").filter((id) => !!id);\n const set = /* @__PURE__ */ new Set();\n for (const id of ids) {\n const firstElement = root.querySelector(\"#\" + CSS.escape(id));\n if (firstElement)\n set.add(firstElement);\n }\n return [...set];\n } catch (e) {\n return [];\n }\n}\nfunction normalizeAccessbileName(s) {\n return s.replace(/\\r\\n/g, \"\\n\").replace(/\\u00A0/g, \" \").replace(/\\s\\s+/g, \" \").trim();\n}\nfunction queryInAriaOwned(element, selector) {\n const result = [...element.querySelectorAll(selector)];\n for (const owned of getIdRefs(element, element.getAttribute(\"aria-owns\"))) {\n if (owned.matches(selector))\n result.push(owned);\n result.push(...owned.querySelectorAll(selector));\n }\n return result;\n}\nfunction getPseudoContent(pseudoStyle) {\n if (!pseudoStyle)\n return \"\";\n const content = pseudoStyle.content;\n if (content[0] === \"'\" && content[content.length - 1] === \"'\" || content[0] === '\"' && content[content.length - 1] === '\"') {\n const unquoted = content.substring(1, content.length - 1);\n const display = pseudoStyle.display || \"inline\";\n if (display !== \"inline\")\n return \" \" + unquoted + \" \";\n return unquoted;\n }\n return \"\";\n}\nfunction getAriaLabelledByElements(element) {\n const ref = element.getAttribute(\"aria-labelledby\");\n if (ref === null)\n return null;\n return getIdRefs(element, ref);\n}\nfunction allowsNameFromContent(role, targetDescendant) {\n const alwaysAllowsNameFromContent = [\"button\", \"cell\", \"checkbox\", \"columnheader\", \"gridcell\", \"heading\", \"link\", \"menuitem\", \"menuitemcheckbox\", \"menuitemradio\", \"option\", \"radio\", \"row\", \"rowheader\", \"switch\", \"tab\", \"tooltip\", \"treeitem\"].includes(role);\n const descendantAllowsNameFromContent = targetDescendant && [\"\", \"caption\", \"code\", \"contentinfo\", \"definition\", \"deletion\", \"emphasis\", \"insertion\", \"list\", \"listitem\", \"mark\", \"none\", \"paragraph\", \"presentation\", \"region\", \"row\", \"rowgroup\", \"section\", \"strong\", \"subscript\", \"superscript\", \"table\", \"term\", \"time\"].includes(role);\n return alwaysAllowsNameFromContent || descendantAllowsNameFromContent;\n}\nfunction getElementAccessibleName(element, includeHidden) {\n const cache = includeHidden ? cacheAccessibleNameHidden : cacheAccessibleName;\n let accessibleName = cache == null ? void 0 : cache.get(element);\n if (accessibleName === void 0) {\n accessibleName = \"\";\n const elementProhibitsNaming = [\"caption\", \"code\", \"definition\", \"deletion\", \"emphasis\", \"generic\", \"insertion\", \"mark\", \"paragraph\", \"presentation\", \"strong\", \"subscript\", \"suggestion\", \"superscript\", \"term\", \"time\"].includes(getAriaRole(element) || \"\");\n if (!elementProhibitsNaming) {\n accessibleName = normalizeAccessbileName(getElementAccessibleNameInternal(element, {\n includeHidden,\n visitedElements: /* @__PURE__ */ new Set(),\n embeddedInLabelledBy: \"none\",\n embeddedInLabel: \"none\",\n embeddedInTextAlternativeElement: false,\n embeddedInTargetElement: \"self\"\n }));\n }\n cache == null ? void 0 : cache.set(element, accessibleName);\n }\n return accessibleName;\n}\nfunction getElementAccessibleNameInternal(element, options) {\n if (options.visitedElements.has(element))\n return \"\";\n const childOptions = {\n ...options,\n embeddedInLabel: options.embeddedInLabel === \"self\" ? \"descendant\" : options.embeddedInLabel,\n embeddedInLabelledBy: options.embeddedInLabelledBy === \"self\" ? \"descendant\" : options.embeddedInLabelledBy,\n embeddedInTargetElement: options.embeddedInTargetElement === \"self\" ? \"descendant\" : options.embeddedInTargetElement\n };\n if (!options.includeHidden && options.embeddedInLabelledBy !== \"self\" && isElementHiddenForAria(element)) {\n options.visitedElements.add(element);\n return \"\";\n }\n const labelledBy = getAriaLabelledByElements(element);\n if (options.embeddedInLabelledBy === \"none\") {\n const accessibleName = (labelledBy || []).map((ref) => getElementAccessibleNameInternal(ref, {\n ...options,\n embeddedInLabelledBy: \"self\",\n embeddedInTargetElement: \"none\",\n embeddedInLabel: \"none\",\n embeddedInTextAlternativeElement: false\n })).join(\" \");\n if (accessibleName)\n return accessibleName;\n }\n const role = getAriaRole(element) || \"\";\n if (options.embeddedInLabel !== \"none\" || options.embeddedInLabelledBy !== \"none\") {\n const isOwnLabel = [...element.labels || []].includes(element);\n const isOwnLabelledBy = (labelledBy || []).includes(element);\n if (!isOwnLabel && !isOwnLabelledBy) {\n if (role === \"textbox\") {\n options.visitedElements.add(element);\n if (element.tagName === \"INPUT\" || element.tagName === \"TEXTAREA\")\n return element.value;\n return element.textContent || \"\";\n }\n if ([\"combobox\", \"listbox\"].includes(role)) {\n options.visitedElements.add(element);\n let selectedOptions;\n if (element.tagName === \"SELECT\") {\n selectedOptions = [...element.selectedOptions];\n if (!selectedOptions.length && element.options.length)\n selectedOptions.push(element.options[0]);\n } else {\n const listbox = role === \"combobox\" ? queryInAriaOwned(element, \"*\").find((e) => getAriaRole(e) === \"listbox\") : element;\n selectedOptions = listbox ? queryInAriaOwned(listbox, '[aria-selected=\"true\"]').filter((e) => getAriaRole(e) === \"option\") : [];\n }\n return selectedOptions.map((option) => getElementAccessibleNameInternal(option, childOptions)).join(\" \");\n }\n if ([\"progressbar\", \"scrollbar\", \"slider\", \"spinbutton\", \"meter\"].includes(role)) {\n options.visitedElements.add(element);\n if (element.hasAttribute(\"aria-valuetext\"))\n return element.getAttribute(\"aria-valuetext\") || \"\";\n if (element.hasAttribute(\"aria-valuenow\"))\n return element.getAttribute(\"aria-valuenow\") || \"\";\n return element.getAttribute(\"value\") || \"\";\n }\n if ([\"menu\"].includes(role)) {\n options.visitedElements.add(element);\n return \"\";\n }\n }\n }\n const ariaLabel = element.getAttribute(\"aria-label\") || \"\";\n if (ariaLabel.trim()) {\n options.visitedElements.add(element);\n return ariaLabel;\n }\n if (![\"presentation\", \"none\"].includes(role)) {\n if (element.tagName === \"INPUT\" && [\"button\", \"submit\", \"reset\"].includes(element.type)) {\n options.visitedElements.add(element);\n const value = element.value || \"\";\n if (value.trim())\n return value;\n if (element.type === \"submit\")\n return \"Submit\";\n if (element.type === \"reset\")\n return \"Reset\";\n const title = element.getAttribute(\"title\") || \"\";\n return title;\n }\n if (element.tagName === \"INPUT\" && element.type === \"image\") {\n options.visitedElements.add(element);\n const labels = element.labels || [];\n if (labels.length && options.embeddedInLabelledBy === \"none\") {\n return [...labels].map((label) => getElementAccessibleNameInternal(label, {\n ...options,\n embeddedInLabel: \"self\",\n embeddedInTextAlternativeElement: false,\n embeddedInLabelledBy: \"none\",\n embeddedInTargetElement: \"none\"\n })).filter((accessibleName) => !!accessibleName).join(\" \");\n }\n const alt = element.getAttribute(\"alt\") || \"\";\n if (alt.trim())\n return alt;\n const title = element.getAttribute(\"title\") || \"\";\n if (title.trim())\n return title;\n return \"Submit\";\n }\n if (!labelledBy && element.tagName === \"BUTTON\") {\n options.visitedElements.add(element);\n const labels = element.labels || [];\n if (labels.length) {\n return [...labels].map((label) => getElementAccessibleNameInternal(label, {\n ...options,\n embeddedInLabel: \"self\",\n embeddedInTextAlternativeElement: false,\n embeddedInLabelledBy: \"none\",\n embeddedInTargetElement: \"none\"\n })).filter((accessibleName) => !!accessibleName).join(\" \");\n }\n }\n if (!labelledBy && (element.tagName === \"TEXTAREA\" || element.tagName === \"SELECT\" || element.tagName === \"INPUT\")) {\n options.visitedElements.add(element);\n const labels = element.labels || [];\n if (labels.length) {\n return [...labels].map((label) => getElementAccessibleNameInternal(label, {\n ...options,\n embeddedInLabel: \"self\",\n embeddedInTextAlternativeElement: false,\n embeddedInLabelledBy: \"none\",\n embeddedInTargetElement: \"none\"\n })).filter((accessibleName) => !!accessibleName).join(\" \");\n }\n const usePlaceholder = element.tagName === \"INPUT\" && [\"text\", \"password\", \"search\", \"tel\", \"email\", \"url\"].includes(element.type) || element.tagName === \"TEXTAREA\";\n const placeholder = element.getAttribute(\"placeholder\") || \"\";\n const title = element.getAttribute(\"title\") || \"\";\n if (!usePlaceholder || title)\n return title;\n return placeholder;\n }\n if (!labelledBy && element.tagName === \"FIELDSET\") {\n options.visitedElements.add(element);\n for (let child = element.firstElementChild; child; child = child.nextElementSibling) {\n if (child.tagName === \"LEGEND\") {\n return getElementAccessibleNameInternal(child, {\n ...childOptions,\n embeddedInTextAlternativeElement: true\n });\n }\n }\n const title = element.getAttribute(\"title\") || \"\";\n return title;\n }\n if (!labelledBy && element.tagName === \"FIGURE\") {\n options.visitedElements.add(element);\n for (let child = element.firstElementChild; child; child = child.nextElementSibling) {\n if (child.tagName === \"FIGCAPTION\") {\n return getElementAccessibleNameInternal(child, {\n ...childOptions,\n embeddedInTextAlternativeElement: true\n });\n }\n }\n const title = element.getAttribute(\"title\") || \"\";\n return title;\n }\n if (element.tagName === \"IMG\") {\n options.visitedElements.add(element);\n const alt = element.getAttribute(\"alt\") || \"\";\n if (alt.trim())\n return alt;\n const title = element.getAttribute(\"title\") || \"\";\n return title;\n }\n if (element.tagName === \"TABLE\") {\n options.visitedElements.add(element);\n for (let child = element.firstElementChild; child; child = child.nextElementSibling) {\n if (child.tagName === \"CAPTION\") {\n return getElementAccessibleNameInternal(child, {\n ...childOptions,\n embeddedInTextAlternativeElement: true\n });\n }\n }\n const summary = element.getAttribute(\"summary\") || \"\";\n if (summary)\n return summary;\n }\n if (element.tagName === \"AREA\") {\n options.visitedElements.add(element);\n const alt = element.getAttribute(\"alt\") || \"\";\n if (alt.trim())\n return alt;\n const title = element.getAttribute(\"title\") || \"\";\n return title;\n }\n if (element.tagName.toUpperCase() === \"SVG\" || element.ownerSVGElement) {\n options.visitedElements.add(element);\n for (let child = element.firstElementChild; child; child = child.nextElementSibling) {\n if (child.tagName.toUpperCase() === \"TITLE\" && child.ownerSVGElement) {\n return getElementAccessibleNameInternal(child, {\n ...childOptions,\n embeddedInLabelledBy: \"self\"\n });\n }\n }\n }\n if (element.ownerSVGElement && element.tagName.toUpperCase() === \"A\") {\n const title = element.getAttribute(\"xlink:title\") || \"\";\n if (title.trim()) {\n options.visitedElements.add(element);\n return title;\n }\n }\n }\n if (allowsNameFromContent(role, options.embeddedInTargetElement === \"descendant\") || options.embeddedInLabelledBy !== \"none\" || options.embeddedInLabel !== \"none\" || options.embeddedInTextAlternativeElement) {\n options.visitedElements.add(element);\n const tokens = [];\n const visit = (node, skipSlotted) => {\n var _a;\n if (skipSlotted && node.assignedSlot)\n return;\n if (node.nodeType === 1) {\n const display = ((_a = getElementComputedStyle(node)) == null ? void 0 : _a.display) || \"inline\";\n let token = getElementAccessibleNameInternal(node, childOptions);\n if (display !== \"inline\" || node.nodeName === \"BR\")\n token = \" \" + token + \" \";\n tokens.push(token);\n } else if (node.nodeType === 3) {\n tokens.push(node.textContent || \"\");\n }\n };\n tokens.push(getPseudoContent(getElementComputedStyle(element, \"::before\")));\n const assignedNodes = element.nodeName === \"SLOT\" ? element.assignedNodes() : [];\n if (assignedNodes.length) {\n for (const child of assignedNodes)\n visit(child, false);\n } else {\n for (let child = element.firstChild; child; child = child.nextSibling)\n visit(child, true);\n if (element.shadowRoot) {\n for (let child = element.shadowRoot.firstChild; child; child = child.nextSibling)\n visit(child, true);\n }\n for (const owned of getIdRefs(element, element.getAttribute(\"aria-owns\")))\n visit(owned, true);\n }\n tokens.push(getPseudoContent(getElementComputedStyle(element, \"::after\")));\n const accessibleName = tokens.join(\"\");\n if (accessibleName.trim())\n return accessibleName;\n }\n if (![\"presentation\", \"none\"].includes(role) || element.tagName === \"IFRAME\") {\n options.visitedElements.add(element);\n const title = element.getAttribute(\"title\") || \"\";\n if (title.trim())\n return title;\n }\n options.visitedElements.add(element);\n return \"\";\n}\nvar kAriaSelectedRoles = [\"gridcell\", \"option\", \"row\", \"tab\", \"rowheader\", \"columnheader\", \"treeitem\"];\nfunction getAriaSelected(element) {\n if (element.tagName === \"OPTION\")\n return element.selected;\n if (kAriaSelectedRoles.includes(getAriaRole(element) || \"\"))\n return getAriaBoolean(element.getAttribute(\"aria-selected\")) === true;\n return false;\n}\nvar kAriaCheckedRoles = [\"checkbox\", \"menuitemcheckbox\", \"option\", \"radio\", \"switch\", \"menuitemradio\", \"treeitem\"];\nfunction getAriaChecked(element) {\n const result = getChecked(element, true);\n return result === \"error\" ? false : result;\n}\nfunction getChecked(element, allowMixed) {\n if (allowMixed && element.tagName === \"INPUT\" && element.indeterminate)\n return \"mixed\";\n if (element.tagName === \"INPUT\" && [\"checkbox\", \"radio\"].includes(element.type))\n return element.checked;\n if (kAriaCheckedRoles.includes(getAriaRole(element) || \"\")) {\n const checked = element.getAttribute(\"aria-checked\");\n if (checked === \"true\")\n return true;\n if (allowMixed && checked === \"mixed\")\n return \"mixed\";\n return false;\n }\n return \"error\";\n}\nvar kAriaPressedRoles = [\"button\"];\nfunction getAriaPressed(element) {\n if (kAriaPressedRoles.includes(getAriaRole(element) || \"\")) {\n const pressed = element.getAttribute(\"aria-pressed\");\n if (pressed === \"true\")\n return true;\n if (pressed === \"mixed\")\n return \"mixed\";\n }\n return false;\n}\nvar kAriaExpandedRoles = [\"application\", \"button\", \"checkbox\", \"combobox\", \"gridcell\", \"link\", \"listbox\", \"menuitem\", \"row\", \"rowheader\", \"tab\", \"treeitem\", \"columnheader\", \"menuitemcheckbox\", \"menuitemradio\", \"rowheader\", \"switch\"];\nfunction getAriaExpanded(element) {\n if (element.tagName === \"DETAILS\")\n return element.open;\n if (kAriaExpandedRoles.includes(getAriaRole(element) || \"\")) {\n const expanded = element.getAttribute(\"aria-expanded\");\n if (expanded === null)\n return \"none\";\n if (expanded === \"true\")\n return true;\n return false;\n }\n return \"none\";\n}\nvar kAriaLevelRoles = [\"heading\", \"listitem\", \"row\", \"treeitem\"];\nfunction getAriaLevel(element) {\n const native = { \"H1\": 1, \"H2\": 2, \"H3\": 3, \"H4\": 4, \"H5\": 5, \"H6\": 6 }[element.tagName];\n if (native)\n return native;\n if (kAriaLevelRoles.includes(getAriaRole(element) || \"\")) {\n const attr = element.getAttribute(\"aria-level\");\n const value = attr === null ? Number.NaN : Number(attr);\n if (Number.isInteger(value) && value >= 1)\n return value;\n }\n return 0;\n}\nvar kAriaDisabledRoles = [\"application\", \"button\", \"composite\", \"gridcell\", \"group\", \"input\", \"link\", \"menuitem\", \"scrollbar\", \"separator\", \"tab\", \"checkbox\", \"columnheader\", \"combobox\", \"grid\", \"listbox\", \"menu\", \"menubar\", \"menuitemcheckbox\", \"menuitemradio\", \"option\", \"radio\", \"radiogroup\", \"row\", \"rowheader\", \"searchbox\", \"select\", \"slider\", \"spinbutton\", \"switch\", \"tablist\", \"textbox\", \"toolbar\", \"tree\", \"treegrid\", \"treeitem\"];\nfunction getAriaDisabled(element) {\n const isNativeFormControl = [\"BUTTON\", \"INPUT\", \"SELECT\", \"TEXTAREA\", \"OPTION\", \"OPTGROUP\"].includes(element.tagName);\n if (isNativeFormControl && (element.hasAttribute(\"disabled\") || belongsToDisabledFieldSet(element)))\n return true;\n return hasExplicitAriaDisabled(element);\n}\nfunction belongsToDisabledFieldSet(element) {\n if (!element)\n return false;\n if (element.tagName === \"FIELDSET\" && element.hasAttribute(\"disabled\"))\n return true;\n return belongsToDisabledFieldSet(element.parentElement);\n}\nfunction hasExplicitAriaDisabled(element) {\n if (!element)\n return false;\n if (kAriaDisabledRoles.includes(getAriaRole(element) || \"\")) {\n const attribute = (element.getAttribute(\"aria-disabled\") || \"\").toLowerCase();\n if (attribute === \"true\")\n return true;\n if (attribute === \"false\")\n return false;\n }\n return hasExplicitAriaDisabled(parentElementOrShadowHost(element));\n}\nvar cacheAccessibleName;\nvar cacheAccessibleNameHidden;\nvar cacheIsHidden;\nvar cachesCounter = 0;\nfunction beginAriaCaches() {\n ++cachesCounter;\n cacheAccessibleName != null ? cacheAccessibleName : cacheAccessibleName = /* @__PURE__ */ new Map();\n cacheAccessibleNameHidden != null ? cacheAccessibleNameHidden : cacheAccessibleNameHidden = /* @__PURE__ */ new Map();\n cacheIsHidden != null ? cacheIsHidden : cacheIsHidden = /* @__PURE__ */ new Map();\n}\nfunction endAriaCaches() {\n if (!--cachesCounter) {\n cacheAccessibleName = void 0;\n cacheAccessibleNameHidden = void 0;\n cacheIsHidden = void 0;\n }\n}\n\n// packages/playwright-core/src/server/injected/selectorUtils.ts\nfunction matchesComponentAttribute(obj, attr) {\n for (const token of attr.jsonPath) {\n if (obj !== void 0 && obj !== null)\n obj = obj[token];\n }\n return matchesAttributePart(obj, attr);\n}\nfunction matchesAttributePart(value, attr) {\n const objValue = typeof value === \"string\" && !attr.caseSensitive ? value.toUpperCase() : value;\n const attrValue = typeof attr.value === \"string\" && !attr.caseSensitive ? attr.value.toUpperCase() : attr.value;\n if (attr.op === \"<truthy>\")\n return !!objValue;\n if (attr.op === \"=\") {\n if (attrValue instanceof RegExp)\n return typeof objValue === \"string\" && !!objValue.match(attrValue);\n return objValue === attrValue;\n }\n if (typeof objValue !== \"string\" || typeof attrValue !== \"string\")\n return false;\n if (attr.op === \"*=\")\n return objValue.includes(attrValue);\n if (attr.op === \"^=\")\n return objValue.startsWith(attrValue);\n if (attr.op === \"$=\")\n return objValue.endsWith(attrValue);\n if (attr.op === \"|=\")\n return objValue === attrValue || objValue.startsWith(attrValue + \"-\");\n if (attr.op === \"~=\")\n return objValue.split(\" \").includes(attrValue);\n return false;\n}\nfunction shouldSkipForTextMatching(element) {\n const document = element.ownerDocument;\n return element.nodeName === \"SCRIPT\" || element.nodeName === \"NOSCRIPT\" || element.nodeName === \"STYLE\" || document.head && document.head.contains(element);\n}\nfunction elementText(cache, root) {\n let value = cache.get(root);\n if (value === void 0) {\n value = { full: \"\", immediate: [] };\n if (!shouldSkipForTextMatching(root)) {\n let currentImmediate = \"\";\n if (root instanceof HTMLInputElement && (root.type === \"submit\" || root.type === \"button\")) {\n value = { full: root.value, immediate: [root.value] };\n } else {\n for (let child = root.firstChild; child; child = child.nextSibling) {\n if (child.nodeType === Node.TEXT_NODE) {\n value.full += child.nodeValue || \"\";\n currentImmediate += child.nodeValue || \"\";\n } else {\n if (currentImmediate)\n value.immediate.push(currentImmediate);\n currentImmediate = \"\";\n if (child.nodeType === Node.ELEMENT_NODE)\n value.full += elementText(cache, child).full;\n }\n }\n if (currentImmediate)\n value.immediate.push(currentImmediate);\n if (root.shadowRoot)\n value.full += elementText(cache, root.shadowRoot).full;\n }\n }\n cache.set(root, value);\n }\n return value;\n}\nfunction elementMatchesText(cache, element, matcher) {\n if (shouldSkipForTextMatching(element))\n return \"none\";\n if (!matcher(elementText(cache, element)))\n return \"none\";\n for (let child = element.firstChild; child; child = child.nextSibling) {\n if (child.nodeType === Node.ELEMENT_NODE && matcher(elementText(cache, child)))\n return \"selfAndChildren\";\n }\n if (element.shadowRoot && matcher(elementText(cache, element.shadowRoot)))\n return \"selfAndChildren\";\n return \"self\";\n}\nfunction getElementLabels(textCache, element) {\n const labels = getAriaLabelledByElements(element);\n if (labels)\n return labels.map((label) => elementText(textCache, label));\n const ariaLabel = element.getAttribute(\"aria-label\");\n if (ariaLabel !== null && !!ariaLabel.trim())\n return [{ full: ariaLabel, immediate: [ariaLabel] }];\n const isNonHiddenInput = element.nodeName === \"INPUT\" && element.type !== \"hidden\";\n if ([\"BUTTON\", \"METER\", \"OUTPUT\", \"PROGRESS\", \"SELECT\", \"TEXTAREA\"].includes(element.nodeName) || isNonHiddenInput) {\n const labels2 = element.labels;\n if (labels2)\n return [...labels2].map((label) => elementText(textCache, label));\n }\n return [];\n}\n\n// packages/playwright-core/src/utils/isomorphic/cssTokenizer.ts\nvar between = function(num, first, last) {\n return num >= first && num <= last;\n};\nfunction digit(code) {\n return between(code, 48, 57);\n}\nfunction hexdigit(code) {\n return digit(code) || between(code, 65, 70) || between(code, 97, 102);\n}\nfunction uppercaseletter(code) {\n return between(code, 65, 90);\n}\nfunction lowercaseletter(code) {\n return between(code, 97, 122);\n}\nfunction letter(code) {\n return uppercaseletter(code) || lowercaseletter(code);\n}\nfunction nonascii(code) {\n return code >= 128;\n}\nfunction namestartchar(code) {\n return letter(code) || nonascii(code) || code === 95;\n}\nfunction namechar(code) {\n return namestartchar(code) || digit(code) || code === 45;\n}\nfunction nonprintable(code) {\n return between(code, 0, 8) || code === 11 || between(code, 14, 31) || code === 127;\n}\nfunction newline(code) {\n return code === 10;\n}\nfunction whitespace(code) {\n return newline(code) || code === 9 || code === 32;\n}\nvar maximumallowedcodepoint = 1114111;\nvar InvalidCharacterError = class extends Error {\n constructor(message) {\n super(message);\n this.name = \"InvalidCharacterError\";\n }\n};\nfunction preprocess(str) {\n const codepoints = [];\n for (let i = 0; i < str.length; i++) {\n let code = str.charCodeAt(i);\n if (code === 13 && str.charCodeAt(i + 1) === 10) {\n code = 10;\n i++;\n }\n if (code === 13 || code === 12)\n code = 10;\n if (code === 0)\n code = 65533;\n if (between(code, 55296, 56319) && between(str.charCodeAt(i + 1), 56320, 57343)) {\n const lead = code - 55296;\n const trail = str.charCodeAt(i + 1) - 56320;\n code = Math.pow(2, 16) + lead * Math.pow(2, 10) + trail;\n i++;\n }\n codepoints.push(code);\n }\n return codepoints;\n}\nfunction stringFromCode(code) {\n if (code <= 65535)\n return String.fromCharCode(code);\n code -= Math.pow(2, 16);\n const lead = Math.floor(code / Math.pow(2, 10)) + 55296;\n const trail = code % Math.pow(2, 10) + 56320;\n return String.fromCharCode(lead) + String.fromCharCode(trail);\n}\nfunction tokenize(str1) {\n const str = preprocess(str1);\n let i = -1;\n const tokens = [];\n let code;\n let line = 0;\n let column = 0;\n let lastLineLength = 0;\n const incrLineno = function() {\n line += 1;\n lastLineLength = column;\n column = 0;\n };\n const locStart = { line, column };\n const codepoint = function(i2) {\n if (i2 >= str.length)\n return -1;\n return str[i2];\n };\n const next = function(num) {\n if (num === void 0)\n num = 1;\n if (num > 3)\n throw \"Spec Error: no more than three codepoints of lookahead.\";\n return codepoint(i + num);\n };\n const consume = function(num) {\n if (num === void 0)\n num = 1;\n i += num;\n code = codepoint(i);\n if (newline(code))\n incrLineno();\n else\n column += num;\n return true;\n };\n const reconsume = function() {\n i -= 1;\n if (newline(code)) {\n line -= 1;\n column = lastLineLength;\n } else {\n column -= 1;\n }\n locStart.line = line;\n locStart.column = column;\n return true;\n };\n const eof = function(codepoint2) {\n if (codepoint2 === void 0)\n codepoint2 = code;\n return codepoint2 === -1;\n };\n const donothing = function() {\n };\n const parseerror = function() {\n };\n const consumeAToken = function() {\n consumeComments();\n consume();\n if (whitespace(code)) {\n while (whitespace(next()))\n consume();\n return new WhitespaceToken();\n } else if (code === 34) {\n return consumeAStringToken();\n } else if (code === 35) {\n if (namechar(next()) || areAValidEscape(next(1), next(2))) {\n const token = new HashToken(\"\");\n if (wouldStartAnIdentifier(next(1), next(2), next(3)))\n token.type = \"id\";\n token.value = consumeAName();\n return token;\n } else {\n return new DelimToken(code);\n }\n } else if (code === 36) {\n if (next() === 61) {\n consume();\n return new SuffixMatchToken();\n } else {\n return new DelimToken(code);\n }\n } else if (code === 39) {\n return consumeAStringToken();\n } else if (code === 40) {\n return new OpenParenToken();\n } else if (code === 41) {\n return new CloseParenToken();\n } else if (code === 42) {\n if (next() === 61) {\n consume();\n return new SubstringMatchToken();\n } else {\n return new DelimToken(code);\n }\n } else if (code === 43) {\n if (startsWithANumber()) {\n reconsume();\n return consumeANumericToken();\n } else {\n return new DelimToken(code);\n }\n } else if (code === 44) {\n return new CommaToken();\n } else if (code === 45) {\n if (startsWithANumber()) {\n reconsume();\n return consumeANumericToken();\n } else if (next(1) === 45 && next(2) === 62) {\n consume(2);\n return new CDCToken();\n } else if (startsWithAnIdentifier()) {\n reconsume();\n return consumeAnIdentlikeToken();\n } else {\n return new DelimToken(code);\n }\n } else if (code === 46) {\n if (startsWithANumber()) {\n reconsume();\n return consumeANumericToken();\n } else {\n return new DelimToken(code);\n }\n } else if (code === 58) {\n return new ColonToken();\n } else if (code === 59) {\n return new SemicolonToken();\n } else if (code === 60) {\n if (next(1) === 33 && next(2) === 45 && next(3) === 45) {\n consume(3);\n return new CDOToken();\n } else {\n return new DelimToken(code);\n }\n } else if (code === 64) {\n if (wouldStartAnIdentifier(next(1), next(2), next(3)))\n return new AtKeywordToken(consumeAName());\n else\n return new DelimToken(code);\n } else if (code === 91) {\n return new OpenSquareToken();\n } else if (code === 92) {\n if (startsWithAValidEscape()) {\n reconsume();\n return consumeAnIdentlikeToken();\n } else {\n parseerror();\n return new DelimToken(code);\n }\n } else if (code === 93) {\n return new CloseSquareToken();\n } else if (code === 94) {\n if (next() === 61) {\n consume();\n return new PrefixMatchToken();\n } else {\n return new DelimToken(code);\n }\n } else if (code === 123) {\n return new OpenCurlyToken();\n } else if (code === 124) {\n if (next() === 61) {\n consume();\n return new DashMatchToken();\n } else if (next() === 124) {\n consume();\n return new ColumnToken();\n } else {\n return new DelimToken(code);\n }\n } else if (code === 125) {\n return new CloseCurlyToken();\n } else if (code === 126) {\n if (next() === 61) {\n consume();\n return new IncludeMatchToken();\n } else {\n return new DelimToken(code);\n }\n } else if (digit(code)) {\n reconsume();\n return consumeANumericToken();\n } else if (namestartchar(code)) {\n reconsume();\n return consumeAnIdentlikeToken();\n } else if (eof()) {\n return new EOFToken();\n } else {\n return new DelimToken(code);\n }\n };\n const consumeComments = function() {\n while (next(1) === 47 && next(2) === 42) {\n consume(2);\n while (true) {\n consume();\n if (code === 42 && next() === 47) {\n consume();\n break;\n } else if (eof()) {\n parseerror();\n return;\n }\n }\n }\n };\n const consumeANumericToken = function() {\n const num = consumeANumber();\n if (wouldStartAnIdentifier(next(1), next(2), next(3))) {\n const token = new DimensionToken();\n token.value = num.value;\n token.repr = num.repr;\n token.type = num.type;\n token.unit = consumeAName();\n return token;\n } else if (next() === 37) {\n consume();\n const token = new PercentageToken();\n token.value = num.value;\n token.repr = num.repr;\n return token;\n } else {\n const token = new NumberToken();\n token.value = num.value;\n token.repr = num.repr;\n token.type = num.type;\n return token;\n }\n };\n const consumeAnIdentlikeToken = function() {\n const str2 = consumeAName();\n if (str2.toLowerCase() === \"url\" && next() === 40) {\n consume();\n while (whitespace(next(1)) && whitespace(next(2)))\n consume();\n if (next() === 34 || next() === 39)\n return new FunctionToken(str2);\n else if (whitespace(next()) && (next(2) === 34 || next(2) === 39))\n return new FunctionToken(str2);\n else\n return consumeAURLToken();\n } else if (next() === 40) {\n consume();\n return new FunctionToken(str2);\n } else {\n return new IdentToken(str2);\n }\n };\n const consumeAStringToken = function(endingCodePoint) {\n if (endingCodePoint === void 0)\n endingCodePoint = code;\n let string = \"\";\n while (consume()) {\n if (code === endingCodePoint || eof()) {\n return new StringToken(string);\n } else if (newline(code)) {\n parseerror();\n reconsume();\n return new BadStringToken();\n } else if (code === 92) {\n if (eof(next()))\n donothing();\n else if (newline(next()))\n consume();\n else\n string += stringFromCode(consumeEscape());\n } else {\n string += stringFromCode(code);\n }\n }\n throw new Error(\"Internal error\");\n };\n const consumeAURLToken = function() {\n const token = new URLToken(\"\");\n while (whitespace(next()))\n consume();\n if (eof(next()))\n return token;\n while (consume()) {\n if (code === 41 || eof()) {\n return token;\n } else if (whitespace(code)) {\n while (whitespace(next()))\n consume();\n if (next() === 41 || eof(next())) {\n consume();\n return token;\n } else {\n consumeTheRemnantsOfABadURL();\n return new BadURLToken();\n }\n } else if (code === 34 || code === 39 || code === 40 || nonprintable(code)) {\n parseerror();\n consumeTheRemnantsOfABadURL();\n return new BadURLToken();\n } else if (code === 92) {\n if (startsWithAValidEscape()) {\n token.value += stringFromCode(consumeEscape());\n } else {\n parseerror();\n consumeTheRemnantsOfABadURL();\n return new BadURLToken();\n }\n } else {\n token.value += stringFromCode(code);\n }\n }\n throw new Error(\"Internal error\");\n };\n const consumeEscape = function() {\n consume();\n if (hexdigit(code)) {\n const digits = [code];\n for (let total = 0; total < 5; total++) {\n if (hexdigit(next())) {\n consume();\n digits.push(code);\n } else {\n break;\n }\n }\n if (whitespace(next()))\n consume();\n let value = parseInt(digits.map(function(x) {\n return String.fromCharCode(x);\n }).join(\"\"), 16);\n if (value > maximumallowedcodepoint)\n value = 65533;\n return value;\n } else if (eof()) {\n return 65533;\n } else {\n return code;\n }\n };\n const areAValidEscape = function(c1, c2) {\n if (c1 !== 92)\n return false;\n if (newline(c2))\n return false;\n return true;\n };\n const startsWithAValidEscape = function() {\n return areAValidEscape(code, next());\n };\n const wouldStartAnIdentifier = function(c1, c2, c3) {\n if (c1 === 45)\n return namestartchar(c2) || c2 === 45 || areAValidEscape(c2, c3);\n else if (namestartchar(c1))\n return true;\n else if (c1 === 92)\n return areAValidEscape(c1, c2);\n else\n return false;\n };\n const startsWithAnIdentifier = function() {\n return wouldStartAnIdentifier(code, next(1), next(2));\n };\n const wouldStartANumber = function(c1, c2, c3) {\n if (c1 === 43 || c1 === 45) {\n if (digit(c2))\n return true;\n if (c2 === 46 && digit(c3))\n return true;\n return false;\n } else if (c1 === 46) {\n if (digit(c2))\n return true;\n return false;\n } else if (digit(c1)) {\n return true;\n } else {\n return false;\n }\n };\n const startsWithANumber = function() {\n return wouldStartANumber(code, next(1), next(2));\n };\n const consumeAName = function() {\n let result = \"\";\n while (consume()) {\n if (namechar(code)) {\n result += stringFromCode(code);\n } else if (startsWithAValidEscape()) {\n result += stringFromCode(consumeEscape());\n } else {\n reconsume();\n return result;\n }\n }\n throw new Error(\"Internal parse error\");\n };\n const consumeANumber = function() {\n let repr = \"\";\n let type = \"integer\";\n if (next() === 43 || next() === 45) {\n consume();\n repr += stringFromCode(code);\n }\n while (digit(next())) {\n consume();\n repr += stringFromCode(code);\n }\n if (next(1) === 46 && digit(next(2))) {\n consume();\n repr += stringFromCode(code);\n consume();\n repr += stringFromCode(code);\n type = \"number\";\n while (digit(next())) {\n consume();\n repr += stringFromCode(code);\n }\n }\n const c1 = next(1), c2 = next(2), c3 = next(3);\n if ((c1 === 69 || c1 === 101) && digit(c2)) {\n consume();\n repr += stringFromCode(code);\n consume();\n repr += stringFromCode(code);\n type = \"number\";\n while (digit(next())) {\n consume();\n repr += stringFromCode(code);\n }\n } else if ((c1 === 69 || c1 === 101) && (c2 === 43 || c2 === 45) && digit(c3)) {\n consume();\n repr += stringFromCode(code);\n consume();\n repr += stringFromCode(code);\n consume();\n repr += stringFromCode(code);\n type = \"number\";\n while (digit(next())) {\n consume();\n repr += stringFromCode(code);\n }\n }\n const value = convertAStringToANumber(repr);\n return { type, value, repr };\n };\n const convertAStringToANumber = function(string) {\n return +string;\n };\n const consumeTheRemnantsOfABadURL = function() {\n while (consume()) {\n if (code === 41 || eof()) {\n return;\n } else if (startsWithAValidEscape()) {\n consumeEscape();\n donothing();\n } else {\n donothing();\n }\n }\n };\n let iterationCount = 0;\n while (!eof(next())) {\n tokens.push(consumeAToken());\n iterationCount++;\n if (iterationCount > str.length * 2)\n throw new Error(\"I'm infinite-looping!\");\n }\n return tokens;\n}\nvar CSSParserToken = class {\n constructor() {\n this.tokenType = \"\";\n }\n toJSON() {\n return { token: this.tokenType };\n }\n toString() {\n return this.tokenType;\n }\n toSource() {\n return \"\" + this;\n }\n};\nvar BadStringToken = class extends CSSParserToken {\n constructor() {\n super(...arguments);\n this.tokenType = \"BADSTRING\";\n }\n};\nvar BadURLToken = class extends CSSParserToken {\n constructor() {\n super(...arguments);\n this.tokenType = \"BADURL\";\n }\n};\nvar WhitespaceToken = class extends CSSParserToken {\n constructor() {\n super(...arguments);\n this.tokenType = \"WHITESPACE\";\n }\n toString() {\n return \"WS\";\n }\n toSource() {\n return \" \";\n }\n};\nvar CDOToken = class extends CSSParserToken {\n constructor() {\n super(...arguments);\n this.tokenType = \"CDO\";\n }\n toSource() {\n return \"<!--\";\n }\n};\nvar CDCToken = class extends CSSParserToken {\n constructor() {\n super(...arguments);\n this.tokenType = \"CDC\";\n }\n toSource() {\n return \"-->\";\n }\n};\nvar ColonToken = class extends CSSParserToken {\n constructor() {\n super(...arguments);\n this.tokenType = \":\";\n }\n};\nvar SemicolonToken = class extends CSSParserToken {\n constructor() {\n super(...arguments);\n this.tokenType = \";\";\n }\n};\nvar CommaToken = class extends CSSParserToken {\n constructor() {\n super(...arguments);\n this.tokenType = \",\";\n }\n};\nvar GroupingToken = class extends CSSParserToken {\n constructor() {\n super(...arguments);\n this.value = \"\";\n this.mirror = \"\";\n }\n};\nvar OpenCurlyToken = class extends GroupingToken {\n constructor() {\n super();\n this.tokenType = \"{\";\n this.value = \"{\";\n this.mirror = \"}\";\n }\n};\nvar CloseCurlyToken = class extends GroupingToken {\n constructor() {\n super();\n this.tokenType = \"}\";\n this.value = \"}\";\n this.mirror = \"{\";\n }\n};\nvar OpenSquareToken = class extends GroupingToken {\n constructor() {\n super();\n this.tokenType = \"[\";\n this.value = \"[\";\n this.mirror = \"]\";\n }\n};\nvar CloseSquareToken = class extends GroupingToken {\n constructor() {\n super();\n this.tokenType = \"]\";\n this.value = \"]\";\n this.mirror = \"[\";\n }\n};\nvar OpenParenToken = class extends GroupingToken {\n constructor() {\n super();\n this.tokenType = \"(\";\n this.value = \"(\";\n this.mirror = \")\";\n }\n};\nvar CloseParenToken = class extends GroupingToken {\n constructor() {\n super();\n this.tokenType = \")\";\n this.value = \")\";\n this.mirror = \"(\";\n }\n};\nvar IncludeMatchToken = class extends CSSParserToken {\n constructor() {\n super(...arguments);\n this.tokenType = \"~=\";\n }\n};\nvar DashMatchToken = class extends CSSParserToken {\n constructor() {\n super(...arguments);\n this.tokenType = \"|=\";\n }\n};\nvar PrefixMatchToken = class extends CSSParserToken {\n constructor() {\n super(...arguments);\n this.tokenType = \"^=\";\n }\n};\nvar SuffixMatchToken = class extends CSSParserToken {\n constructor() {\n super(...arguments);\n this.tokenType = \"$=\";\n }\n};\nvar SubstringMatchToken = class extends CSSParserToken {\n constructor() {\n super(...arguments);\n this.tokenType = \"*=\";\n }\n};\nvar ColumnToken = class extends CSSParserToken {\n constructor() {\n super(...arguments);\n this.tokenType = \"||\";\n }\n};\nvar EOFToken = class extends CSSParserToken {\n constructor() {\n super(...arguments);\n this.tokenType = \"EOF\";\n }\n toSource() {\n return \"\";\n }\n};\nvar DelimToken = class extends CSSParserToken {\n constructor(code) {\n super();\n this.tokenType = \"DELIM\";\n this.value = \"\";\n this.value = stringFromCode(code);\n }\n toString() {\n return \"DELIM(\" + this.value + \")\";\n }\n toJSON() {\n const json = this.constructor.prototype.constructor.prototype.toJSON.call(this);\n json.value = this.value;\n return json;\n }\n toSource() {\n if (this.value === \"\\\\\")\n return \"\\\\\\n\";\n else\n return this.value;\n }\n};\nvar StringValuedToken = class extends CSSParserToken {\n constructor() {\n super(...arguments);\n this.value = \"\";\n }\n ASCIIMatch(str) {\n return this.value.toLowerCase() === str.toLowerCase();\n }\n toJSON() {\n const json = this.constructor.prototype.constructor.prototype.toJSON.call(this);\n json.value = this.value;\n return json;\n }\n};\nvar IdentToken = class extends StringValuedToken {\n constructor(val) {\n super();\n this.tokenType = \"IDENT\";\n this.value = val;\n }\n toString() {\n return \"IDENT(\" + this.value + \")\";\n }\n toSource() {\n return escapeIdent(this.value);\n }\n};\nvar FunctionToken = class extends StringValuedToken {\n constructor(val) {\n super();\n this.tokenType = \"FUNCTION\";\n this.value = val;\n this.mirror = \")\";\n }\n toString() {\n return \"FUNCTION(\" + this.value + \")\";\n }\n toSource() {\n return escapeIdent(this.value) + \"(\";\n }\n};\nvar AtKeywordToken = class extends StringValuedToken {\n constructor(val) {\n super();\n this.tokenType = \"AT-KEYWORD\";\n this.value = val;\n }\n toString() {\n return \"AT(\" + this.value + \")\";\n }\n toSource() {\n return \"@\" + escapeIdent(this.value);\n }\n};\nvar HashToken = class extends StringValuedToken {\n constructor(val) {\n super();\n this.tokenType = \"HASH\";\n this.value = val;\n this.type = \"unrestricted\";\n }\n toString() {\n return \"HASH(\" + this.value + \")\";\n }\n toJSON() {\n const json = this.constructor.prototype.constructor.prototype.toJSON.call(this);\n json.value = this.value;\n json.type = this.type;\n return json;\n }\n toSource() {\n if (this.type === \"id\")\n return \"#\" + escapeIdent(this.value);\n else\n return \"#\" + escapeHash(this.value);\n }\n};\nvar StringToken = class extends StringValuedToken {\n constructor(val) {\n super();\n this.tokenType = \"STRING\";\n this.value = val;\n }\n toString() {\n return '\"' + escapeString(this.value) + '\"';\n }\n};\nvar URLToken = class extends StringValuedToken {\n constructor(val) {\n super();\n this.tokenType = \"URL\";\n this.value = val;\n }\n toString() {\n return \"URL(\" + this.value + \")\";\n }\n toSource() {\n return 'url(\"' + escapeString(this.value) + '\")';\n }\n};\nvar NumberToken = class extends CSSParserToken {\n constructor() {\n super();\n this.tokenType = \"NUMBER\";\n this.type = \"integer\";\n this.repr = \"\";\n }\n toString() {\n if (this.type === \"integer\")\n return \"INT(\" + this.value + \")\";\n return \"NUMBER(\" + this.value + \")\";\n }\n toJSON() {\n const json = super.toJSON();\n json.value = this.value;\n json.type = this.type;\n json.repr = this.repr;\n return json;\n }\n toSource() {\n return this.repr;\n }\n};\nvar PercentageToken = class extends CSSParserToken {\n constructor() {\n super();\n this.tokenType = \"PERCENTAGE\";\n this.repr = \"\";\n }\n toString() {\n return \"PERCENTAGE(\" + this.value + \")\";\n }\n toJSON() {\n const json = this.constructor.prototype.constructor.prototype.toJSON.call(this);\n json.value = this.value;\n json.repr = this.repr;\n return json;\n }\n toSource() {\n return this.repr + \"%\";\n }\n};\nvar DimensionToken = class extends CSSParserToken {\n constructor() {\n super();\n this.tokenType = \"DIMENSION\";\n this.type = \"integer\";\n this.repr = \"\";\n this.unit = \"\";\n }\n toString() {\n return \"DIM(\" + this.value + \",\" + this.unit + \")\";\n }\n toJSON() {\n const json = this.constructor.prototype.constructor.prototype.toJSON.call(this);\n json.value = this.value;\n json.type = this.type;\n json.repr = this.repr;\n json.unit = this.unit;\n return json;\n }\n toSource() {\n const source = this.repr;\n let unit = escapeIdent(this.unit);\n if (unit[0].toLowerCase() === \"e\" && (unit[1] === \"-\" || between(unit.charCodeAt(1), 48, 57))) {\n unit = \"\\\\65 \" + unit.slice(1, unit.length);\n }\n return source + unit;\n }\n};\nfunction escapeIdent(string) {\n string = \"\" + string;\n let result = \"\";\n const firstcode = string.charCodeAt(0);\n for (let i = 0; i < string.length; i++) {\n const code = string.charCodeAt(i);\n if (code === 0)\n throw new InvalidCharacterError(\"Invalid character: the input contains U+0000.\");\n if (between(code, 1, 31) || code === 127 || i === 0 && between(code, 48, 57) || i === 1 && between(code, 48, 57) && firstcode === 45)\n result += \"\\\\\" + code.toString(16) + \" \";\n else if (code >= 128 || code === 45 || code === 95 || between(code, 48, 57) || between(code, 65, 90) || between(code, 97, 122))\n result += string[i];\n else\n result += \"\\\\\" + string[i];\n }\n return result;\n}\nfunction escapeHash(string) {\n string = \"\" + string;\n let result = \"\";\n for (let i = 0; i < string.length; i++) {\n const code = string.charCodeAt(i);\n if (code === 0)\n throw new InvalidCharacterError(\"Invalid character: the input contains U+0000.\");\n if (code >= 128 || code === 45 || code === 95 || between(code, 48, 57) || between(code, 65, 90) || between(code, 97, 122))\n result += string[i];\n else\n result += \"\\\\\" + code.toString(16) + \" \";\n }\n return result;\n}\nfunction escapeString(string) {\n string = \"\" + string;\n let result = \"\";\n for (let i = 0; i < string.length; i++) {\n const code = string.charCodeAt(i);\n if (code === 0)\n throw new InvalidCharacterError(\"Invalid character: the input contains U+0000.\");\n if (between(code, 1, 31) || code === 127)\n result += \"\\\\\" + code.toString(16) + \" \";\n else if (code === 34 || code === 92)\n result += \"\\\\\" + string[i];\n else\n result += string[i];\n }\n return result;\n}\n\n// packages/playwright-core/src/utils/isomorphic/cssParser.ts\nvar InvalidSelectorError = class extends Error {\n};\nfunction parseCSS(selector, customNames) {\n let tokens;\n try {\n tokens = tokenize(selector);\n if (!(tokens[tokens.length - 1] instanceof EOFToken))\n tokens.push(new EOFToken());\n } catch (e) {\n const newMessage = e.message + ` while parsing selector \"${selector}\"`;\n const index = (e.stack || \"\").indexOf(e.message);\n if (index !== -1)\n e.stack = e.stack.substring(0, index) + newMessage + e.stack.substring(index + e.message.length);\n e.message = newMessage;\n throw e;\n }\n const unsupportedToken = tokens.find((token) => {\n return token instanceof AtKeywordToken || token instanceof BadStringToken || token instanceof BadURLToken || token instanceof ColumnToken || token instanceof CDOToken || token instanceof CDCToken || token instanceof SemicolonToken || token instanceof OpenCurlyToken || token instanceof CloseCurlyToken || token instanceof URLToken || token instanceof PercentageToken;\n });\n if (unsupportedToken)\n throw new InvalidSelectorError(`Unsupported token \"${unsupportedToken.toSource()}\" while parsing selector \"${selector}\"`);\n let pos = 0;\n const names = /* @__PURE__ */ new Set();\n function unexpected() {\n return new InvalidSelectorError(`Unexpected token \"${tokens[pos].toSource()}\" while parsing selector \"${selector}\"`);\n }\n function skipWhitespace() {\n while (tokens[pos] instanceof WhitespaceToken)\n pos++;\n }\n function isIdent(p = pos) {\n return tokens[p] instanceof IdentToken;\n }\n function isString(p = pos) {\n return tokens[p] instanceof StringToken;\n }\n function isNumber(p = pos) {\n return tokens[p] instanceof NumberToken;\n }\n function isComma(p = pos) {\n return tokens[p] instanceof CommaToken;\n }\n function isCloseParen(p = pos) {\n return tokens[p] instanceof CloseParenToken;\n }\n function isStar(p = pos) {\n return tokens[p] instanceof DelimToken && tokens[p].value === \"*\";\n }\n function isEOF(p = pos) {\n return tokens[p] instanceof EOFToken;\n }\n function isClauseCombinator(p = pos) {\n return tokens[p] instanceof DelimToken && [\">\", \"+\", \"~\"].includes(tokens[p].value);\n }\n function isSelectorClauseEnd(p = pos) {\n return isComma(p) || isCloseParen(p) || isEOF(p) || isClauseCombinator(p) || tokens[p] instanceof WhitespaceToken;\n }\n function consumeFunctionArguments() {\n const result2 = [consumeArgument()];\n while (true) {\n skipWhitespace();\n if (!isComma())\n break;\n pos++;\n result2.push(consumeArgument());\n }\n return result2;\n }\n function consumeArgument() {\n skipWhitespace();\n if (isNumber())\n return tokens[pos++].value;\n if (isString())\n return tokens[pos++].value;\n return consumeComplexSelector();\n }\n function consumeComplexSelector() {\n const result2 = { simples: [] };\n skipWhitespace();\n if (isClauseCombinator()) {\n result2.simples.push({ selector: { functions: [{ name: \"scope\", args: [] }] }, combinator: \"\" });\n } else {\n result2.simples.push({ selector: consumeSimpleSelector(), combinator: \"\" });\n }\n while (true) {\n skipWhitespace();\n if (isClauseCombinator()) {\n result2.simples[result2.simples.length - 1].combinator = tokens[pos++].value;\n skipWhitespace();\n } else if (isSelectorClauseEnd()) {\n break;\n }\n result2.simples.push({ combinator: \"\", selector: consumeSimpleSelector() });\n }\n return result2;\n }\n function consumeSimpleSelector() {\n let rawCSSString = \"\";\n const functions = [];\n while (!isSelectorClauseEnd()) {\n if (isIdent() || isStar()) {\n rawCSSString += tokens[pos++].toSource();\n } else if (tokens[pos] instanceof HashToken) {\n rawCSSString += tokens[pos++].toSource();\n } else if (tokens[pos] instanceof DelimToken && tokens[pos].value === \".\") {\n pos++;\n if (isIdent())\n rawCSSString += \".\" + tokens[pos++].toSource();\n else\n throw unexpected();\n } else if (tokens[pos] instanceof ColonToken) {\n pos++;\n if (isIdent()) {\n if (!customNames.has(tokens[pos].value.toLowerCase())) {\n rawCSSString += \":\" + tokens[pos++].toSource();\n } else {\n const name = tokens[pos++].value.toLowerCase();\n functions.push({ name, args: [] });\n names.add(name);\n }\n } else if (tokens[pos] instanceof FunctionToken) {\n const name = tokens[pos++].value.toLowerCase();\n if (!customNames.has(name)) {\n rawCSSString += `:${name}(${consumeBuiltinFunctionArguments()})`;\n } else {\n functions.push({ name, args: consumeFunctionArguments() });\n names.add(name);\n }\n skipWhitespace();\n if (!isCloseParen())\n throw unexpected();\n pos++;\n } else {\n throw unexpected();\n }\n } else if (tokens[pos] instanceof OpenSquareToken) {\n rawCSSString += \"[\";\n pos++;\n while (!(tokens[pos] instanceof CloseSquareToken) && !isEOF())\n rawCSSString += tokens[pos++].toSource();\n if (!(tokens[pos] instanceof CloseSquareToken))\n throw unexpected();\n rawCSSString += \"]\";\n pos++;\n } else {\n throw unexpected();\n }\n }\n if (!rawCSSString && !functions.length)\n throw unexpected();\n return { css: rawCSSString || void 0, functions };\n }\n function consumeBuiltinFunctionArguments() {\n let s = \"\";\n while (!isCloseParen() && !isEOF())\n s += tokens[pos++].toSource();\n return s;\n }\n const result = consumeFunctionArguments();\n if (!isEOF())\n throw new InvalidSelectorError(`Error while parsing selector \"${selector}\"`);\n if (result.some((arg) => typeof arg !== \"object\" || !(\"simples\" in arg)))\n throw new InvalidSelectorError(`Error while parsing selector \"${selector}\"`);\n return { selector: result, names: Array.from(names) };\n}\n\n// packages/playwright-core/src/utils/isomorphic/selectorParser.ts\nvar kNestedSelectorNames = /* @__PURE__ */ new Set([\"internal:has\", \"internal:has-not\", \"internal:and\", \"internal:or\", \"left-of\", \"right-of\", \"above\", \"below\", \"near\"]);\nvar kNestedSelectorNamesWithDistance = /* @__PURE__ */ new Set([\"left-of\", \"right-of\", \"above\", \"below\", \"near\"]);\nvar customCSSNames = /* @__PURE__ */ new Set([\"not\", \"is\", \"where\", \"has\", \"scope\", \"light\", \"visible\", \"text\", \"text-matches\", \"text-is\", \"has-text\", \"above\", \"below\", \"right-of\", \"left-of\", \"near\", \"nth-match\"]);\nfunction parseSelector(selector) {\n const parsedStrings = parseSelectorString(selector);\n const parts = [];\n for (const part of parsedStrings.parts) {\n if (part.name === \"css\" || part.name === \"css:light\") {\n if (part.name === \"css:light\")\n part.body = \":light(\" + part.body + \")\";\n const parsedCSS = parseCSS(part.body, customCSSNames);\n parts.push({\n name: \"css\",\n body: parsedCSS.selector,\n source: part.body\n });\n continue;\n }\n if (kNestedSelectorNames.has(part.name)) {\n let innerSelector;\n let distance;\n try {\n const unescaped = JSON.parse(\"[\" + part.body + \"]\");\n if (!Array.isArray(unescaped) || unescaped.length < 1 || unescaped.length > 2 || typeof unescaped[0] !== \"string\")\n throw new InvalidSelectorError(`Malformed selector: ${part.name}=` + part.body);\n innerSelector = unescaped[0];\n if (unescaped.length === 2) {\n if (typeof unescaped[1] !== \"number\" || !kNestedSelectorNamesWithDistance.has(part.name))\n throw new InvalidSelectorError(`Malformed selector: ${part.name}=` + part.body);\n distance = unescaped[1];\n }\n } catch (e) {\n throw new InvalidSelectorError(`Malformed selector: ${part.name}=` + part.body);\n }\n const nested = { name: part.name, source: part.body, body: { parsed: parseSelector(innerSelector), distance } };\n const lastFrame = [...nested.body.parsed.parts].reverse().find((part2) => part2.name === \"internal:control\" && part2.body === \"enter-frame\");\n const lastFrameIndex = lastFrame ? nested.body.parsed.parts.indexOf(lastFrame) : -1;\n if (lastFrameIndex !== -1 && selectorPartsEqual(nested.body.parsed.parts.slice(0, lastFrameIndex + 1), parts.slice(0, lastFrameIndex + 1)))\n nested.body.parsed.parts.splice(0, lastFrameIndex + 1);\n parts.push(nested);\n continue;\n }\n parts.push({ ...part, source: part.body });\n }\n if (kNestedSelectorNames.has(parts[0].name))\n throw new InvalidSelectorError(`\"${parts[0].name}\" selector cannot be first`);\n return {\n capture: parsedStrings.capture,\n parts\n };\n}\nfunction selectorPartsEqual(list1, list2) {\n return stringifySelector({ parts: list1 }) === stringifySelector({ parts: list2 });\n}\nfunction stringifySelector(selector) {\n if (typeof selector === \"string\")\n return selector;\n return selector.parts.map((p, i) => {\n const prefix = p.name === \"css\" ? \"\" : p.name + \"=\";\n return `${i === selector.capture ? \"*\" : \"\"}${prefix}${p.source}`;\n }).join(\" >> \");\n}\nfunction visitAllSelectorParts(selector, visitor) {\n const visit = (selector2, nested) => {\n for (const part of selector2.parts) {\n visitor(part, nested);\n if (kNestedSelectorNames.has(part.name))\n visit(part.body.parsed, true);\n }\n };\n visit(selector, false);\n}\nfunction parseSelectorString(selector) {\n let index = 0;\n let quote;\n let start = 0;\n const result = { parts: [] };\n const append = () => {\n const part = selector.substring(start, index).trim();\n const eqIndex = part.indexOf(\"=\");\n let name;\n let body;\n if (eqIndex !== -1 && part.substring(0, eqIndex).trim().match(/^[a-zA-Z_0-9-+:*]+$/)) {\n name = part.substring(0, eqIndex).trim();\n body = part.substring(eqIndex + 1);\n } else if (part.length > 1 && part[0] === '\"' && part[part.length - 1] === '\"') {\n name = \"text\";\n body = part;\n } else if (part.length > 1 && part[0] === \"'\" && part[part.length - 1] === \"'\") {\n name = \"text\";\n body = part;\n } else if (/^\\(*\\/\\//.test(part) || part.startsWith(\"..\")) {\n name = \"xpath\";\n body = part;\n } else {\n name = \"css\";\n body = part;\n }\n let capture = false;\n if (name[0] === \"*\") {\n capture = true;\n name = name.substring(1);\n }\n result.parts.push({ name, body });\n if (capture) {\n if (result.capture !== void 0)\n throw new InvalidSelectorError(`Only one of the selectors can capture using * modifier`);\n result.capture = result.parts.length - 1;\n }\n };\n if (!selector.includes(\">>\")) {\n index = selector.length;\n append();\n return result;\n }\n const shouldIgnoreTextSelectorQuote = () => {\n const prefix = selector.substring(start, index);\n const match = prefix.match(/^\\s*text\\s*=(.*)$/);\n return !!match && !!match[1];\n };\n while (index < selector.length) {\n const c = selector[index];\n if (c === \"\\\\\" && index + 1 < selector.length) {\n index += 2;\n } else if (c === quote) {\n quote = void 0;\n index++;\n } else if (!quote && (c === '\"' || c === \"'\" || c === \"`\") && !shouldIgnoreTextSelectorQuote()) {\n quote = c;\n index++;\n } else if (!quote && c === \">\" && selector[index + 1] === \">\") {\n append();\n index += 2;\n start = index;\n } else {\n index++;\n }\n }\n append();\n return result;\n}\nfunction parseAttributeSelector(selector, allowUnquotedStrings) {\n let wp = 0;\n let EOL = selector.length === 0;\n const next = () => selector[wp] || \"\";\n const eat1 = () => {\n const result2 = next();\n ++wp;\n EOL = wp >= selector.length;\n return result2;\n };\n const syntaxError = (stage) => {\n if (EOL)\n throw new InvalidSelectorError(`Unexpected end of selector while parsing selector \\`${selector}\\``);\n throw new InvalidSelectorError(`Error while parsing selector \\`${selector}\\` - unexpected symbol \"${next()}\" at position ${wp}` + (stage ? \" during \" + stage : \"\"));\n };\n function skipSpaces() {\n while (!EOL && /\\s/.test(next()))\n eat1();\n }\n function isCSSNameChar(char) {\n return char >= \"\\x80\" || char >= \"0\" && char <= \"9\" || char >= \"A\" && char <= \"Z\" || char >= \"a\" && char <= \"z\" || char >= \"0\" && char <= \"9\" || char === \"_\" || char === \"-\";\n }\n function readIdentifier() {\n let result2 = \"\";\n skipSpaces();\n while (!EOL && isCSSNameChar(next()))\n result2 += eat1();\n return result2;\n }\n function readQuotedString(quote) {\n let result2 = eat1();\n if (result2 !== quote)\n syntaxError(\"parsing quoted string\");\n while (!EOL && next() !== quote) {\n if (next() === \"\\\\\")\n eat1();\n result2 += eat1();\n }\n if (next() !== quote)\n syntaxError(\"parsing quoted string\");\n result2 += eat1();\n return result2;\n }\n function readRegularExpression() {\n if (eat1() !== \"/\")\n syntaxError(\"parsing regular expression\");\n let source = \"\";\n let inClass = false;\n while (!EOL) {\n if (next() === \"\\\\\") {\n source += eat1();\n if (EOL)\n syntaxError(\"parsing regular expressiion\");\n } else if (inClass && next() === \"]\") {\n inClass = false;\n } else if (!inClass && next() === \"[\") {\n inClass = true;\n } else if (!inClass && next() === \"/\") {\n break;\n }\n source += eat1();\n }\n if (eat1() !== \"/\")\n syntaxError(\"parsing regular expression\");\n let flags = \"\";\n while (!EOL && next().match(/[dgimsuy]/))\n flags += eat1();\n try {\n return new RegExp(source, flags);\n } catch (e) {\n throw new InvalidSelectorError(`Error while parsing selector \\`${selector}\\`: ${e.message}`);\n }\n }\n function readAttributeToken() {\n let token = \"\";\n skipSpaces();\n if (next() === `'` || next() === `\"`)\n token = readQuotedString(next()).slice(1, -1);\n else\n token = readIdentifier();\n if (!token)\n syntaxError(\"parsing property path\");\n return token;\n }\n function readOperator() {\n skipSpaces();\n let op = \"\";\n if (!EOL)\n op += eat1();\n if (!EOL && op !== \"=\")\n op += eat1();\n if (![\"=\", \"*=\", \"^=\", \"$=\", \"|=\", \"~=\"].includes(op))\n syntaxError(\"parsing operator\");\n return op;\n }\n function readAttribute() {\n eat1();\n const jsonPath = [];\n jsonPath.push(readAttributeToken());\n skipSpaces();\n while (next() === \".\") {\n eat1();\n jsonPath.push(readAttributeToken());\n skipSpaces();\n }\n if (next() === \"]\") {\n eat1();\n return { name: jsonPath.join(\".\"), jsonPath, op: \"<truthy>\", value: null, caseSensitive: false };\n }\n const operator = readOperator();\n let value = void 0;\n let caseSensitive = true;\n skipSpaces();\n if (next() === \"/\") {\n if (operator !== \"=\")\n throw new InvalidSelectorError(`Error while parsing selector \\`${selector}\\` - cannot use ${operator} in attribute with regular expression`);\n value = readRegularExpression();\n } else if (next() === `'` || next() === `\"`) {\n value = readQuotedString(next()).slice(1, -1);\n skipSpaces();\n if (next() === \"i\" || next() === \"I\") {\n caseSensitive = false;\n eat1();\n } else if (next() === \"s\" || next() === \"S\") {\n caseSensitive = true;\n eat1();\n }\n } else {\n value = \"\";\n while (!EOL && (isCSSNameChar(next()) || next() === \"+\" || next() === \".\"))\n value += eat1();\n if (value === \"true\") {\n value = true;\n } else if (value === \"false\") {\n value = false;\n } else {\n if (!allowUnquotedStrings) {\n value = +value;\n if (Number.isNaN(value))\n syntaxError(\"parsing attribute value\");\n }\n }\n }\n skipSpaces();\n if (next() !== \"]\")\n syntaxError(\"parsing attribute value\");\n eat1();\n if (operator !== \"=\" && typeof value !== \"string\")\n throw new InvalidSelectorError(`Error while parsing selector \\`${selector}\\` - cannot use ${operator} in attribute with non-string matching value - ${value}`);\n return { name: jsonPath.join(\".\"), jsonPath, op: operator, value, caseSensitive };\n }\n const result = {\n name: \"\",\n attributes: []\n };\n result.name = readIdentifier();\n skipSpaces();\n while (next() === \"[\") {\n result.attributes.push(readAttribute());\n skipSpaces();\n }\n if (!EOL)\n syntaxError(void 0);\n if (!result.name && !result.attributes.length)\n throw new InvalidSelectorError(`Error while parsing selector \\`${selector}\\` - selector cannot be empty`);\n return result;\n}\n\n// packages/playwright-core/src/server/injected/reactSelectorEngine.ts\nfunction getFunctionComponentName(component) {\n return component.displayName || component.name || \"Anonymous\";\n}\nfunction getComponentName(reactElement) {\n if (reactElement.type) {\n switch (typeof reactElement.type) {\n case \"function\":\n return getFunctionComponentName(reactElement.type);\n case \"string\":\n return reactElement.type;\n case \"object\":\n return reactElement.type.displayName || (reactElement.type.render ? getFunctionComponentName(reactElement.type.render) : \"\");\n }\n }\n if (reactElement._currentElement) {\n const elementType = reactElement._currentElement.type;\n if (typeof elementType === \"string\")\n return elementType;\n if (typeof elementType === \"function\")\n return elementType.displayName || elementType.name || \"Anonymous\";\n }\n return \"\";\n}\nfunction getComponentKey(reactElement) {\n var _a, _b;\n return (_b = reactElement.key) != null ? _b : (_a = reactElement._currentElement) == null ? void 0 : _a.key;\n}\nfunction getChildren(reactElement) {\n if (reactElement.child) {\n const children = [];\n for (let child = reactElement.child; child; child = child.sibling)\n children.push(child);\n return children;\n }\n if (!reactElement._currentElement)\n return [];\n const isKnownElement = (reactElement2) => {\n var _a;\n const elementType = (_a = reactElement2._currentElement) == null ? void 0 : _a.type;\n return typeof elementType === \"function\" || typeof elementType === \"string\";\n };\n if (reactElement._renderedComponent) {\n const child = reactElement._renderedComponent;\n return isKnownElement(child) ? [child] : [];\n }\n if (reactElement._renderedChildren)\n return [...Object.values(reactElement._renderedChildren)].filter(isKnownElement);\n return [];\n}\nfunction getProps(reactElement) {\n var _a;\n const props = reactElement.memoizedProps || ((_a = reactElement._currentElement) == null ? void 0 : _a.props);\n if (!props || typeof props === \"string\")\n return props;\n const result = { ...props };\n delete result.children;\n return result;\n}\nfunction buildComponentsTree(reactElement) {\n var _a;\n const treeNode = {\n key: getComponentKey(reactElement),\n name: getComponentName(reactElement),\n children: getChildren(reactElement).map(buildComponentsTree),\n rootElements: [],\n props: getProps(reactElement)\n };\n const rootElement = reactElement.stateNode || reactElement._hostNode || ((_a = reactElement._renderedComponent) == null ? void 0 : _a._hostNode);\n if (rootElement instanceof Element) {\n treeNode.rootElements.push(rootElement);\n } else {\n for (const child of treeNode.children)\n treeNode.rootElements.push(...child.rootElements);\n }\n return treeNode;\n}\nfunction filterComponentsTree(treeNode, searchFn, result = []) {\n if (searchFn(treeNode))\n result.push(treeNode);\n for (const child of treeNode.children)\n filterComponentsTree(child, searchFn, result);\n return result;\n}\nfunction findReactRoots(root, roots = []) {\n const document = root.ownerDocument || root;\n const walker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT);\n do {\n const node = walker.currentNode;\n const reactNode = node;\n const rootKey = Object.keys(reactNode).find((key) => key.startsWith(\"__reactContainer\") && reactNode[key] !== null);\n if (rootKey) {\n roots.push(reactNode[rootKey].stateNode.current);\n } else {\n const legacyRootKey = \"_reactRootContainer\";\n if (reactNode.hasOwnProperty(legacyRootKey) && reactNode[legacyRootKey] !== null) {\n roots.push(reactNode[legacyRootKey]._internalRoot.current);\n }\n }\n if (node instanceof Element && node.hasAttribute(\"data-reactroot\")) {\n for (const key of Object.keys(node)) {\n if (key.startsWith(\"__reactInternalInstance\") || key.startsWith(\"__reactFiber\"))\n roots.push(node[key]);\n }\n }\n const shadowRoot = node instanceof Element ? node.shadowRoot : null;\n if (shadowRoot)\n findReactRoots(shadowRoot, roots);\n } while (walker.nextNode());\n return roots;\n}\nvar ReactEngine = {\n queryAll(scope, selector) {\n const { name, attributes } = parseAttributeSelector(selector, false);\n const reactRoots = findReactRoots(scope.ownerDocument || scope);\n const trees = reactRoots.map((reactRoot) => buildComponentsTree(reactRoot));\n const treeNodes = trees.map((tree) => filterComponentsTree(tree, (treeNode) => {\n var _a;\n const props = (_a = treeNode.props) != null ? _a : {};\n if (treeNode.key !== void 0)\n props.key = treeNode.key;\n if (name && treeNode.name !== name)\n return false;\n if (treeNode.rootElements.some((domNode) => !isInsideScope(scope, domNode)))\n return false;\n for (const attr of attributes) {\n if (!matchesComponentAttribute(props, attr))\n return false;\n }\n return true;\n })).flat();\n const allRootElements = /* @__PURE__ */ new Set();\n for (const treeNode of treeNodes) {\n for (const domNode of treeNode.rootElements)\n allRootElements.add(domNode);\n }\n return [...allRootElements];\n }\n};\n\n// packages/playwright-core/src/server/injected/vueSelectorEngine.ts\nfunction basename(filename, ext) {\n const normalized = filename.replace(/^[a-zA-Z]:/, \"\").replace(/\\\\/g, \"/\");\n let result = normalized.substring(normalized.lastIndexOf(\"/\") + 1);\n if (ext && result.endsWith(ext))\n result = result.substring(0, result.length - ext.length);\n return result;\n}\nfunction toUpper(_, c) {\n return c ? c.toUpperCase() : \"\";\n}\nvar classifyRE = /(?:^|[-_/])(\\w)/g;\nvar classify = (str) => {\n return str && str.replace(classifyRE, toUpper);\n};\nfunction buildComponentsTreeVue3(instance) {\n function getComponentTypeName(options) {\n const name = options.name || options._componentTag || options.__playwright_guessedName;\n if (name)\n return name;\n const file = options.__file;\n if (file)\n return classify(basename(file, \".vue\"));\n }\n function saveComponentName(instance2, key) {\n instance2.type.__playwright_guessedName = key;\n return key;\n }\n function getInstanceName(instance2) {\n var _a, _b, _c, _d;\n const name = getComponentTypeName(instance2.type || {});\n if (name)\n return name;\n if (instance2.root === instance2)\n return \"Root\";\n for (const key in (_b = (_a = instance2.parent) == null ? void 0 : _a.type) == null ? void 0 : _b.components)\n if (((_c = instance2.parent) == null ? void 0 : _c.type.components[key]) === instance2.type)\n return saveComponentName(instance2, key);\n for (const key in (_d = instance2.appContext) == null ? void 0 : _d.components)\n if (instance2.appContext.components[key] === instance2.type)\n return saveComponentName(instance2, key);\n return \"Anonymous Component\";\n }\n function isBeingDestroyed(instance2) {\n return instance2._isBeingDestroyed || instance2.isUnmounted;\n }\n function isFragment(instance2) {\n return instance2.subTree.type.toString() === \"Symbol(Fragment)\";\n }\n function getInternalInstanceChildren(subTree) {\n const list = [];\n if (subTree.component)\n list.push(subTree.component);\n if (subTree.suspense)\n list.push(...getInternalInstanceChildren(subTree.suspense.activeBranch));\n if (Array.isArray(subTree.children)) {\n subTree.children.forEach((childSubTree) => {\n if (childSubTree.component)\n list.push(childSubTree.component);\n else\n list.push(...getInternalInstanceChildren(childSubTree));\n });\n }\n return list.filter((child) => {\n var _a;\n return !isBeingDestroyed(child) && !((_a = child.type.devtools) == null ? void 0 : _a.hide);\n });\n }\n function getRootElementsFromComponentInstance(instance2) {\n if (isFragment(instance2))\n return getFragmentRootElements(instance2.subTree);\n return [instance2.subTree.el];\n }\n function getFragmentRootElements(vnode) {\n if (!vnode.children)\n return [];\n const list = [];\n for (let i = 0, l = vnode.children.length; i < l; i++) {\n const childVnode = vnode.children[i];\n if (childVnode.component)\n list.push(...getRootElementsFromComponentInstance(childVnode.component));\n else if (childVnode.el)\n list.push(childVnode.el);\n }\n return list;\n }\n function buildComponentsTree2(instance2) {\n return {\n name: getInstanceName(instance2),\n children: getInternalInstanceChildren(instance2.subTree).map(buildComponentsTree2),\n rootElements: getRootElementsFromComponentInstance(instance2),\n props: instance2.props\n };\n }\n return buildComponentsTree2(instance);\n}\nfunction buildComponentsTreeVue2(instance) {\n function getComponentName2(options) {\n const name = options.displayName || options.name || options._componentTag;\n if (name)\n return name;\n const file = options.__file;\n if (file)\n return classify(basename(file, \".vue\"));\n }\n function getInstanceName(instance2) {\n const name = getComponentName2(instance2.$options || instance2.fnOptions || {});\n if (name)\n return name;\n return instance2.$root === instance2 ? \"Root\" : \"Anonymous Component\";\n }\n function getInternalInstanceChildren(instance2) {\n if (instance2.$children)\n return instance2.$children;\n if (Array.isArray(instance2.subTree.children))\n return instance2.subTree.children.filter((vnode) => !!vnode.component).map((vnode) => vnode.component);\n return [];\n }\n function buildComponentsTree2(instance2) {\n return {\n name: getInstanceName(instance2),\n children: getInternalInstanceChildren(instance2).map(buildComponentsTree2),\n rootElements: [instance2.$el],\n props: instance2._props\n };\n }\n return buildComponentsTree2(instance);\n}\nfunction filterComponentsTree2(treeNode, searchFn, result = []) {\n if (searchFn(treeNode))\n result.push(treeNode);\n for (const child of treeNode.children)\n filterComponentsTree2(child, searchFn, result);\n return result;\n}\nfunction findVueRoots(root, roots = []) {\n const document = root.ownerDocument || root;\n const walker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT);\n const vue2Roots = /* @__PURE__ */ new Set();\n do {\n const node = walker.currentNode;\n if (node.__vue__)\n vue2Roots.add(node.__vue__.$root);\n if (node.__vue_app__ && node._vnode && node._vnode.component)\n roots.push({ root: node._vnode.component, version: 3 });\n const shadowRoot = node instanceof Element ? node.shadowRoot : null;\n if (shadowRoot)\n findVueRoots(shadowRoot, roots);\n } while (walker.nextNode());\n for (const vue2root of vue2Roots) {\n roots.push({\n version: 2,\n root: vue2root\n });\n }\n return roots;\n}\nvar VueEngine = {\n queryAll(scope, selector) {\n const document = scope.ownerDocument || scope;\n const { name, attributes } = parseAttributeSelector(selector, false);\n const vueRoots = findVueRoots(document);\n const trees = vueRoots.map((vueRoot) => vueRoot.version === 3 ? buildComponentsTreeVue3(vueRoot.root) : buildComponentsTreeVue2(vueRoot.root));\n const treeNodes = trees.map((tree) => filterComponentsTree2(tree, (treeNode) => {\n if (name && treeNode.name !== name)\n return false;\n if (treeNode.rootElements.some((rootElement) => !isInsideScope(scope, rootElement)))\n return false;\n for (const attr of attributes) {\n if (!matchesComponentAttribute(treeNode.props, attr))\n return false;\n }\n return true;\n })).flat();\n const allRootElements = /* @__PURE__ */ new Set();\n for (const treeNode of treeNodes) {\n for (const rootElement of treeNode.rootElements)\n allRootElements.add(rootElement);\n }\n return [...allRootElements];\n }\n};\n\n// packages/playwright-core/src/utils/isomorphic/stringUtils.ts\nfunction escapeWithQuotes(text, char = \"'\") {\n const stringified = JSON.stringify(text);\n const escapedText = stringified.substring(1, stringified.length - 1).replace(/\\\\\"/g, '\"');\n if (char === \"'\")\n return char + escapedText.replace(/[']/g, \"\\\\'\") + char;\n if (char === '\"')\n return char + escapedText.replace(/[\"]/g, '\\\\\"') + char;\n if (char === \"`\")\n return char + escapedText.replace(/[`]/g, \"`\") + char;\n throw new Error(\"Invalid escape char\");\n}\nfunction toTitleCase(name) {\n return name.charAt(0).toUpperCase() + name.substring(1);\n}\nfunction toSnakeCase(name) {\n return name.replace(/([a-z0-9])([A-Z])/g, \"$1_$2\").replace(/([A-Z])([A-Z][a-z])/g, \"$1_$2\").toLowerCase();\n}\nfunction cssEscape(s) {\n let result = \"\";\n for (let i = 0; i < s.length; i++)\n result += cssEscapeOne(s, i);\n return result;\n}\nfunction cssEscapeOne(s, i) {\n const c = s.charCodeAt(i);\n if (c === 0)\n return \"\\uFFFD\";\n if (c >= 1 && c <= 31 || c >= 48 && c <= 57 && (i === 0 || i === 1 && s.charCodeAt(0) === 45))\n return \"\\\\\" + c.toString(16) + \" \";\n if (i === 0 && c === 45 && s.length === 1)\n return \"\\\\\" + s.charAt(i);\n if (c >= 128 || c === 45 || c === 95 || c >= 48 && c <= 57 || c >= 65 && c <= 90 || c >= 97 && c <= 122)\n return s.charAt(i);\n return \"\\\\\" + s.charAt(i);\n}\nfunction normalizeWhiteSpace(text) {\n return text.replace(/\\u200b/g, \"\").trim().replace(/\\s+/g, \" \");\n}\nfunction escapeForTextSelector(text, exact) {\n if (typeof text !== \"string\")\n return String(text).replace(/>>/g, \"\\\\>\\\\>\");\n return `${JSON.stringify(text)}${exact ? \"s\" : \"i\"}`;\n}\nfunction escapeForAttributeSelector(value, exact) {\n if (typeof value !== \"string\")\n return String(value).replace(/>>/g, \"\\\\>\\\\>\");\n return `\"${value.replace(/\\\\/g, \"\\\\\\\\\").replace(/[\"]/g, '\\\\\"')}\"${exact ? \"s\" : \"i\"}`;\n}\n\n// packages/playwright-core/src/server/injected/roleSelectorEngine.ts\nvar kSupportedAttributes = [\"selected\", \"checked\", \"pressed\", \"expanded\", \"level\", \"disabled\", \"name\", \"include-hidden\"];\nkSupportedAttributes.sort();\nfunction validateSupportedRole(attr, roles, role) {\n if (!roles.includes(role))\n throw new Error(`\"${attr}\" attribute is only supported for roles: ${roles.slice().sort().map((role2) => `\"${role2}\"`).join(\", \")}`);\n}\nfunction validateSupportedValues(attr, values) {\n if (attr.op !== \"<truthy>\" && !values.includes(attr.value))\n throw new Error(`\"${attr.name}\" must be one of ${values.map((v) => JSON.stringify(v)).join(\", \")}`);\n}\nfunction validateSupportedOp(attr, ops) {\n if (!ops.includes(attr.op))\n throw new Error(`\"${attr.name}\" does not support \"${attr.op}\" matcher`);\n}\nfunction validateAttributes(attrs, role) {\n const options = { role };\n for (const attr of attrs) {\n switch (attr.name) {\n case \"checked\": {\n validateSupportedRole(attr.name, kAriaCheckedRoles, role);\n validateSupportedValues(attr, [true, false, \"mixed\"]);\n validateSupportedOp(attr, [\"<truthy>\", \"=\"]);\n options.checked = attr.op === \"<truthy>\" ? true : attr.value;\n break;\n }\n case \"pressed\": {\n validateSupportedRole(attr.name, kAriaPressedRoles, role);\n validateSupportedValues(attr, [true, false, \"mixed\"]);\n validateSupportedOp(attr, [\"<truthy>\", \"=\"]);\n options.pressed = attr.op === \"<truthy>\" ? true : attr.value;\n break;\n }\n case \"selected\": {\n validateSupportedRole(attr.name, kAriaSelectedRoles, role);\n validateSupportedValues(attr, [true, false]);\n validateSupportedOp(attr, [\"<truthy>\", \"=\"]);\n options.selected = attr.op === \"<truthy>\" ? true : attr.value;\n break;\n }\n case \"expanded\": {\n validateSupportedRole(attr.name, kAriaExpandedRoles, role);\n validateSupportedValues(attr, [true, false]);\n validateSupportedOp(attr, [\"<truthy>\", \"=\"]);\n options.expanded = attr.op === \"<truthy>\" ? true : attr.value;\n break;\n }\n case \"level\": {\n validateSupportedRole(attr.name, kAriaLevelRoles, role);\n if (typeof attr.value === \"string\")\n attr.value = +attr.value;\n if (attr.op !== \"=\" || typeof attr.value !== \"number\" || Number.isNaN(attr.value))\n throw new Error(`\"level\" attribute must be compared to a number`);\n options.level = attr.value;\n break;\n }\n case \"disabled\": {\n validateSupportedValues(attr, [true, false]);\n validateSupportedOp(attr, [\"<truthy>\", \"=\"]);\n options.disabled = attr.op === \"<truthy>\" ? true : attr.value;\n break;\n }\n case \"name\": {\n if (attr.op === \"<truthy>\")\n throw new Error(`\"name\" attribute must have a value`);\n if (typeof attr.value !== \"string\" && !(attr.value instanceof RegExp))\n throw new Error(`\"name\" attribute must be a string or a regular expression`);\n options.name = attr.value;\n options.nameOp = attr.op;\n options.exact = attr.caseSensitive;\n break;\n }\n case \"include-hidden\": {\n validateSupportedValues(attr, [true, false]);\n validateSupportedOp(attr, [\"<truthy>\", \"=\"]);\n options.includeHidden = attr.op === \"<truthy>\" ? true : attr.value;\n break;\n }\n default: {\n throw new Error(`Unknown attribute \"${attr.name}\", must be one of ${kSupportedAttributes.map((a) => `\"${a}\"`).join(\", \")}.`);\n }\n }\n }\n return options;\n}\nfunction queryRole(scope, options, internal) {\n const result = [];\n const match = (element) => {\n if (getAriaRole(element) !== options.role)\n return;\n if (options.selected !== void 0 && getAriaSelected(element) !== options.selected)\n return;\n if (options.checked !== void 0 && getAriaChecked(element) !== options.checked)\n return;\n if (options.pressed !== void 0 && getAriaPressed(element) !== options.pressed)\n return;\n if (options.expanded !== void 0 && getAriaExpanded(element) !== options.expanded)\n return;\n if (options.level !== void 0 && getAriaLevel(element) !== options.level)\n return;\n if (options.disabled !== void 0 && getAriaDisabled(element) !== options.disabled)\n return;\n if (!options.includeHidden) {\n const isHidden = isElementHiddenForAria(element);\n if (isHidden)\n return;\n }\n if (options.name !== void 0) {\n const accessibleName = normalizeWhiteSpace(getElementAccessibleName(element, !!options.includeHidden));\n if (typeof options.name === \"string\")\n options.name = normalizeWhiteSpace(options.name);\n if (internal && !options.exact && options.nameOp === \"=\")\n options.nameOp = \"*=\";\n if (!matchesAttributePart(accessibleName, { name: \"\", jsonPath: [], op: options.nameOp || \"=\", value: options.name, caseSensitive: !!options.exact }))\n return;\n }\n result.push(element);\n };\n const query = (root) => {\n const shadows = [];\n if (root.shadowRoot)\n shadows.push(root.shadowRoot);\n for (const element of root.querySelectorAll(\"*\")) {\n match(element);\n if (element.shadowRoot)\n shadows.push(element.shadowRoot);\n }\n shadows.forEach(query);\n };\n query(scope);\n return result;\n}\nfunction createRoleEngine(internal) {\n return {\n queryAll: (scope, selector) => {\n const parsed = parseAttributeSelector(selector, true);\n const role = parsed.name.toLowerCase();\n if (!role)\n throw new Error(`Role must not be empty`);\n const options = validateAttributes(parsed.attributes, role);\n beginAriaCaches();\n try {\n return queryRole(scope, options, internal);\n } finally {\n endAriaCaches();\n }\n }\n };\n}\n\n// packages/playwright-core/src/server/injected/layoutSelectorUtils.ts\nfunction boxRightOf(box1, box2, maxDistance) {\n const distance = box1.left - box2.right;\n if (distance < 0 || maxDistance !== void 0 && distance > maxDistance)\n return;\n return distance + Math.max(box2.bottom - box1.bottom, 0) + Math.max(box1.top - box2.top, 0);\n}\nfunction boxLeftOf(box1, box2, maxDistance) {\n const distance = box2.left - box1.right;\n if (distance < 0 || maxDistance !== void 0 && distance > maxDistance)\n return;\n return distance + Math.max(box2.bottom - box1.bottom, 0) + Math.max(box1.top - box2.top, 0);\n}\nfunction boxAbove(box1, box2, maxDistance) {\n const distance = box2.top - box1.bottom;\n if (distance < 0 || maxDistance !== void 0 && distance > maxDistance)\n return;\n return distance + Math.max(box1.left - box2.left, 0) + Math.max(box2.right - box1.right, 0);\n}\nfunction boxBelow(box1, box2, maxDistance) {\n const distance = box1.top - box2.bottom;\n if (distance < 0 || maxDistance !== void 0 && distance > maxDistance)\n return;\n return distance + Math.max(box1.left - box2.left, 0) + Math.max(box2.right - box1.right, 0);\n}\nfunction boxNear(box1, box2, maxDistance) {\n const kThreshold = maxDistance === void 0 ? 50 : maxDistance;\n let score = 0;\n if (box1.left - box2.right >= 0)\n score += box1.left - box2.right;\n if (box2.left - box1.right >= 0)\n score += box2.left - box1.right;\n if (box2.top - box1.bottom >= 0)\n score += box2.top - box1.bottom;\n if (box1.top - box2.bottom >= 0)\n score += box1.top - box2.bottom;\n return score > kThreshold ? void 0 : score;\n}\nvar kLayoutSelectorNames = [\"left-of\", \"right-of\", \"above\", \"below\", \"near\"];\nfunction layoutSelectorScore(name, element, inner, maxDistance) {\n const box = element.getBoundingClientRect();\n const scorer = { \"left-of\": boxLeftOf, \"right-of\": boxRightOf, \"above\": boxAbove, \"below\": boxBelow, \"near\": boxNear }[name];\n let bestScore;\n for (const e of inner) {\n if (e === element)\n continue;\n const score = scorer(box, e.getBoundingClientRect(), maxDistance);\n if (score === void 0)\n continue;\n if (bestScore === void 0 || score < bestScore)\n bestScore = score;\n }\n return bestScore;\n}\n\n// packages/playwright-core/src/server/injected/selectorEvaluator.ts\nvar SelectorEvaluatorImpl = class {\n constructor(extraEngines) {\n this._engines = /* @__PURE__ */ new Map();\n this._cacheQueryCSS = /* @__PURE__ */ new Map();\n this._cacheMatches = /* @__PURE__ */ new Map();\n this._cacheQuery = /* @__PURE__ */ new Map();\n this._cacheMatchesSimple = /* @__PURE__ */ new Map();\n this._cacheMatchesParents = /* @__PURE__ */ new Map();\n this._cacheCallMatches = /* @__PURE__ */ new Map();\n this._cacheCallQuery = /* @__PURE__ */ new Map();\n this._cacheQuerySimple = /* @__PURE__ */ new Map();\n this._cacheText = /* @__PURE__ */ new Map();\n this._retainCacheCounter = 0;\n for (const [name, engine] of extraEngines)\n this._engines.set(name, engine);\n this._engines.set(\"not\", notEngine);\n this._engines.set(\"is\", isEngine);\n this._engines.set(\"where\", isEngine);\n this._engines.set(\"has\", hasEngine);\n this._engines.set(\"scope\", scopeEngine);\n this._engines.set(\"light\", lightEngine);\n this._engines.set(\"visible\", visibleEngine);\n this._engines.set(\"text\", textEngine);\n this._engines.set(\"text-is\", textIsEngine);\n this._engines.set(\"text-matches\", textMatchesEngine);\n this._engines.set(\"has-text\", hasTextEngine);\n this._engines.set(\"right-of\", createLayoutEngine(\"right-of\"));\n this._engines.set(\"left-of\", createLayoutEngine(\"left-of\"));\n this._engines.set(\"above\", createLayoutEngine(\"above\"));\n this._engines.set(\"below\", createLayoutEngine(\"below\"));\n this._engines.set(\"near\", createLayoutEngine(\"near\"));\n this._engines.set(\"nth-match\", nthMatchEngine);\n const allNames = [...this._engines.keys()];\n allNames.sort();\n const parserNames = [...customCSSNames];\n parserNames.sort();\n if (allNames.join(\"|\") !== parserNames.join(\"|\"))\n throw new Error(`Please keep customCSSNames in sync with evaluator engines: ${allNames.join(\"|\")} vs ${parserNames.join(\"|\")}`);\n }\n begin() {\n ++this._retainCacheCounter;\n }\n end() {\n --this._retainCacheCounter;\n if (!this._retainCacheCounter) {\n this._cacheQueryCSS.clear();\n this._cacheMatches.clear();\n this._cacheQuery.clear();\n this._cacheMatchesSimple.clear();\n this._cacheMatchesParents.clear();\n this._cacheCallMatches.clear();\n this._cacheCallQuery.clear();\n this._cacheQuerySimple.clear();\n this._cacheText.clear();\n }\n }\n _cached(cache, main, rest, cb) {\n if (!cache.has(main))\n cache.set(main, []);\n const entries = cache.get(main);\n const entry = entries.find((e) => rest.every((value, index) => e.rest[index] === value));\n if (entry)\n return entry.result;\n const result = cb();\n entries.push({ rest, result });\n return result;\n }\n _checkSelector(s) {\n const wellFormed = typeof s === \"object\" && s && (Array.isArray(s) || \"simples\" in s && s.simples.length);\n if (!wellFormed)\n throw new Error(`Malformed selector \"${s}\"`);\n return s;\n }\n matches(element, s, context) {\n const selector = this._checkSelector(s);\n this.begin();\n try {\n return this._cached(this._cacheMatches, element, [selector, context.scope, context.pierceShadow, context.originalScope], () => {\n if (Array.isArray(selector))\n return this._matchesEngine(isEngine, element, selector, context);\n if (this._hasScopeClause(selector))\n context = this._expandContextForScopeMatching(context);\n if (!this._matchesSimple(element, selector.simples[selector.simples.length - 1].selector, context))\n return false;\n return this._matchesParents(element, selector, selector.simples.length - 2, context);\n });\n } finally {\n this.end();\n }\n }\n query(context, s) {\n const selector = this._checkSelector(s);\n this.begin();\n try {\n return this._cached(this._cacheQuery, selector, [context.scope, context.pierceShadow, context.originalScope], () => {\n if (Array.isArray(selector))\n return this._queryEngine(isEngine, context, selector);\n if (this._hasScopeClause(selector))\n context = this._expandContextForScopeMatching(context);\n const previousScoreMap = this._scoreMap;\n this._scoreMap = /* @__PURE__ */ new Map();\n let elements = this._querySimple(context, selector.simples[selector.simples.length - 1].selector);\n elements = elements.filter((element) => this._matchesParents(element, selector, selector.simples.length - 2, context));\n if (this._scoreMap.size) {\n elements.sort((a, b) => {\n const aScore = this._scoreMap.get(a);\n const bScore = this._scoreMap.get(b);\n if (aScore === bScore)\n return 0;\n if (aScore === void 0)\n return 1;\n if (bScore === void 0)\n return -1;\n return aScore - bScore;\n });\n }\n this._scoreMap = previousScoreMap;\n return elements;\n });\n } finally {\n this.end();\n }\n }\n _markScore(element, score) {\n if (this._scoreMap)\n this._scoreMap.set(element, score);\n }\n _hasScopeClause(selector) {\n return selector.simples.some((simple) => simple.selector.functions.some((f) => f.name === \"scope\"));\n }\n _expandContextForScopeMatching(context) {\n if (context.scope.nodeType !== 1)\n return context;\n const scope = parentElementOrShadowHost(context.scope);\n if (!scope)\n return context;\n return { ...context, scope, originalScope: context.originalScope || context.scope };\n }\n _matchesSimple(element, simple, context) {\n return this._cached(this._cacheMatchesSimple, element, [simple, context.scope, context.pierceShadow, context.originalScope], () => {\n if (element === context.scope)\n return false;\n if (simple.css && !this._matchesCSS(element, simple.css))\n return false;\n for (const func of simple.functions) {\n if (!this._matchesEngine(this._getEngine(func.name), element, func.args, context))\n return false;\n }\n return true;\n });\n }\n _querySimple(context, simple) {\n if (!simple.functions.length)\n return this._queryCSS(context, simple.css || \"*\");\n return this._cached(this._cacheQuerySimple, simple, [context.scope, context.pierceShadow, context.originalScope], () => {\n let css = simple.css;\n const funcs = simple.functions;\n if (css === \"*\" && funcs.length)\n css = void 0;\n let elements;\n let firstIndex = -1;\n if (css !== void 0) {\n elements = this._queryCSS(context, css);\n } else {\n firstIndex = funcs.findIndex((func) => this._getEngine(func.name).query !== void 0);\n if (firstIndex === -1)\n firstIndex = 0;\n elements = this._queryEngine(this._getEngine(funcs[firstIndex].name), context, funcs[firstIndex].args);\n }\n for (let i = 0; i < funcs.length; i++) {\n if (i === firstIndex)\n continue;\n const engine = this._getEngine(funcs[i].name);\n if (engine.matches !== void 0)\n elements = elements.filter((e) => this._matchesEngine(engine, e, funcs[i].args, context));\n }\n for (let i = 0; i < funcs.length; i++) {\n if (i === firstIndex)\n continue;\n const engine = this._getEngine(funcs[i].name);\n if (engine.matches === void 0)\n elements = elements.filter((e) => this._matchesEngine(engine, e, funcs[i].args, context));\n }\n return elements;\n });\n }\n _matchesParents(element, complex, index, context) {\n if (index < 0)\n return true;\n return this._cached(this._cacheMatchesParents, element, [complex, index, context.scope, context.pierceShadow, context.originalScope], () => {\n const { selector: simple, combinator } = complex.simples[index];\n if (combinator === \">\") {\n const parent = parentElementOrShadowHostInContext(element, context);\n if (!parent || !this._matchesSimple(parent, simple, context))\n return false;\n return this._matchesParents(parent, complex, index - 1, context);\n }\n if (combinator === \"+\") {\n const previousSibling = previousSiblingInContext(element, context);\n if (!previousSibling || !this._matchesSimple(previousSibling, simple, context))\n return false;\n return this._matchesParents(previousSibling, complex, index - 1, context);\n }\n if (combinator === \"\") {\n let parent = parentElementOrShadowHostInContext(element, context);\n while (parent) {\n if (this._matchesSimple(parent, simple, context)) {\n if (this._matchesParents(parent, complex, index - 1, context))\n return true;\n if (complex.simples[index - 1].combinator === \"\")\n break;\n }\n parent = parentElementOrShadowHostInContext(parent, context);\n }\n return false;\n }\n if (combinator === \"~\") {\n let previousSibling = previousSiblingInContext(element, context);\n while (previousSibling) {\n if (this._matchesSimple(previousSibling, simple, context)) {\n if (this._matchesParents(previousSibling, complex, index - 1, context))\n return true;\n if (complex.simples[index - 1].combinator === \"~\")\n break;\n }\n previousSibling = previousSiblingInContext(previousSibling, context);\n }\n return false;\n }\n if (combinator === \">=\") {\n let parent = element;\n while (parent) {\n if (this._matchesSimple(parent, simple, context)) {\n if (this._matchesParents(parent, complex, index - 1, context))\n return true;\n if (complex.simples[index - 1].combinator === \"\")\n break;\n }\n parent = parentElementOrShadowHostInContext(parent, context);\n }\n return false;\n }\n throw new Error(`Unsupported combinator \"${combinator}\"`);\n });\n }\n _matchesEngine(engine, element, args, context) {\n if (engine.matches)\n return this._callMatches(engine, element, args, context);\n if (engine.query)\n return this._callQuery(engine, args, context).includes(element);\n throw new Error(`Selector engine should implement \"matches\" or \"query\"`);\n }\n _queryEngine(engine, context, args) {\n if (engine.query)\n return this._callQuery(engine, args, context);\n if (engine.matches)\n return this._queryCSS(context, \"*\").filter((element) => this._callMatches(engine, element, args, context));\n throw new Error(`Selector engine should implement \"matches\" or \"query\"`);\n }\n _callMatches(engine, element, args, context) {\n return this._cached(this._cacheCallMatches, element, [engine, context.scope, context.pierceShadow, context.originalScope, ...args], () => {\n return engine.matches(element, args, context, this);\n });\n }\n _callQuery(engine, args, context) {\n return this._cached(this._cacheCallQuery, engine, [context.scope, context.pierceShadow, context.originalScope, ...args], () => {\n return engine.query(context, args, this);\n });\n }\n _matchesCSS(element, css) {\n return element.matches(css);\n }\n _queryCSS(context, css) {\n return this._cached(this._cacheQueryCSS, css, [context.scope, context.pierceShadow, context.originalScope], () => {\n let result = [];\n function query(root) {\n result = result.concat([...root.querySelectorAll(css)]);\n if (!context.pierceShadow)\n return;\n if (root.shadowRoot)\n query(root.shadowRoot);\n for (const element of root.querySelectorAll(\"*\")) {\n if (element.shadowRoot)\n query(element.shadowRoot);\n }\n }\n query(context.scope);\n return result;\n });\n }\n _getEngine(name) {\n const engine = this._engines.get(name);\n if (!engine)\n throw new Error(`Unknown selector engine \"${name}\"`);\n return engine;\n }\n};\nvar isEngine = {\n matches(element, args, context, evaluator) {\n if (args.length === 0)\n throw new Error(`\"is\" engine expects non-empty selector list`);\n return args.some((selector) => evaluator.matches(element, selector, context));\n },\n query(context, args, evaluator) {\n if (args.length === 0)\n throw new Error(`\"is\" engine expects non-empty selector list`);\n let elements = [];\n for (const arg of args)\n elements = elements.concat(evaluator.query(context, arg));\n return args.length === 1 ? elements : sortInDOMOrder(elements);\n }\n};\nvar hasEngine = {\n matches(element, args, context, evaluator) {\n if (args.length === 0)\n throw new Error(`\"has\" engine expects non-empty selector list`);\n return evaluator.query({ ...context, scope: element }, args).length > 0;\n }\n};\nvar scopeEngine = {\n matches(element, args, context, evaluator) {\n if (args.length !== 0)\n throw new Error(`\"scope\" engine expects no arguments`);\n const actualScope = context.originalScope || context.scope;\n if (actualScope.nodeType === 9)\n return element === actualScope.documentElement;\n return element === actualScope;\n },\n query(context, args, evaluator) {\n if (args.length !== 0)\n throw new Error(`\"scope\" engine expects no arguments`);\n const actualScope = context.originalScope || context.scope;\n if (actualScope.nodeType === 9) {\n const root = actualScope.documentElement;\n return root ? [root] : [];\n }\n if (actualScope.nodeType === 1)\n return [actualScope];\n return [];\n }\n};\nvar notEngine = {\n matches(element, args, context, evaluator) {\n if (args.length === 0)\n throw new Error(`\"not\" engine expects non-empty selector list`);\n return !evaluator.matches(element, args, context);\n }\n};\nvar lightEngine = {\n query(context, args, evaluator) {\n return evaluator.query({ ...context, pierceShadow: false }, args);\n },\n matches(element, args, context, evaluator) {\n return evaluator.matches(element, args, { ...context, pierceShadow: false });\n }\n};\nvar visibleEngine = {\n matches(element, args, context, evaluator) {\n if (args.length)\n throw new Error(`\"visible\" engine expects no arguments`);\n return isElementVisible(element);\n }\n};\nvar textEngine = {\n matches(element, args, context, evaluator) {\n if (args.length !== 1 || typeof args[0] !== \"string\")\n throw new Error(`\"text\" engine expects a single string`);\n const text = normalizeWhiteSpace(args[0]).toLowerCase();\n const matcher = (elementText2) => normalizeWhiteSpace(elementText2.full).toLowerCase().includes(text);\n return elementMatchesText(evaluator._cacheText, element, matcher) === \"self\";\n }\n};\nvar textIsEngine = {\n matches(element, args, context, evaluator) {\n if (args.length !== 1 || typeof args[0] !== \"string\")\n throw new Error(`\"text-is\" engine expects a single string`);\n const text = normalizeWhiteSpace(args[0]);\n const matcher = (elementText2) => {\n if (!text && !elementText2.immediate.length)\n return true;\n return elementText2.immediate.some((s) => normalizeWhiteSpace(s) === text);\n };\n return elementMatchesText(evaluator._cacheText, element, matcher) !== \"none\";\n }\n};\nvar textMatchesEngine = {\n matches(element, args, context, evaluator) {\n if (args.length === 0 || typeof args[0] !== \"string\" || args.length > 2 || args.length === 2 && typeof args[1] !== \"string\")\n throw new Error(`\"text-matches\" engine expects a regexp body and optional regexp flags`);\n const re = new RegExp(args[0], args.length === 2 ? args[1] : void 0);\n const matcher = (elementText2) => re.test(elementText2.full);\n return elementMatchesText(evaluator._cacheText, element, matcher) === \"self\";\n }\n};\nvar hasTextEngine = {\n matches(element, args, context, evaluator) {\n if (args.length !== 1 || typeof args[0] !== \"string\")\n throw new Error(`\"has-text\" engine expects a single string`);\n if (shouldSkipForTextMatching(element))\n return false;\n const text = normalizeWhiteSpace(args[0]).toLowerCase();\n const matcher = (elementText2) => normalizeWhiteSpace(elementText2.full).toLowerCase().includes(text);\n return matcher(elementText(evaluator._cacheText, element));\n }\n};\nfunction createLayoutEngine(name) {\n return {\n matches(element, args, context, evaluator) {\n const maxDistance = args.length && typeof args[args.length - 1] === \"number\" ? args[args.length - 1] : void 0;\n const queryArgs = maxDistance === void 0 ? args : args.slice(0, args.length - 1);\n if (args.length < 1 + (maxDistance === void 0 ? 0 : 1))\n throw new Error(`\"${name}\" engine expects a selector list and optional maximum distance in pixels`);\n const inner = evaluator.query(context, queryArgs);\n const score = layoutSelectorScore(name, element, inner, maxDistance);\n if (score === void 0)\n return false;\n evaluator._markScore(element, score);\n return true;\n }\n };\n}\nvar nthMatchEngine = {\n query(context, args, evaluator) {\n let index = args[args.length - 1];\n if (args.length < 2)\n throw new Error(`\"nth-match\" engine expects non-empty selector list and an index argument`);\n if (typeof index !== \"number\" || index < 1)\n throw new Error(`\"nth-match\" engine expects a one-based index as the last argument`);\n const elements = isEngine.query(context, args.slice(0, args.length - 1), evaluator);\n index--;\n return index < elements.length ? [elements[index]] : [];\n }\n};\nfunction parentElementOrShadowHostInContext(element, context) {\n if (element === context.scope)\n return;\n if (!context.pierceShadow)\n return element.parentElement || void 0;\n return parentElementOrShadowHost(element);\n}\nfunction previousSiblingInContext(element, context) {\n if (element === context.scope)\n return;\n return element.previousElementSibling || void 0;\n}\nfunction sortInDOMOrder(elements) {\n const elementToEntry = /* @__PURE__ */ new Map();\n const roots = [];\n const result = [];\n function append(element) {\n let entry = elementToEntry.get(element);\n if (entry)\n return entry;\n const parent = parentElementOrShadowHost(element);\n if (parent) {\n const parentEntry = append(parent);\n parentEntry.children.push(element);\n } else {\n roots.push(element);\n }\n entry = { children: [], taken: false };\n elementToEntry.set(element, entry);\n return entry;\n }\n for (const e of elements)\n append(e).taken = true;\n function visit(element) {\n const entry = elementToEntry.get(element);\n if (entry.taken)\n result.push(element);\n if (entry.children.length > 1) {\n const set = new Set(entry.children);\n entry.children = [];\n let child = element.firstElementChild;\n while (child && entry.children.length < set.size) {\n if (set.has(child))\n entry.children.push(child);\n child = child.nextElementSibling;\n }\n child = element.shadowRoot ? element.shadowRoot.firstElementChild : null;\n while (child && entry.children.length < set.size) {\n if (set.has(child))\n entry.children.push(child);\n child = child.nextElementSibling;\n }\n }\n entry.children.forEach(visit);\n }\n roots.forEach(visit);\n return result;\n}\n\n// packages/playwright-core/src/server/injected/selectorGenerator.ts\nvar cacheAllowText = /* @__PURE__ */ new Map();\nvar cacheDisallowText = /* @__PURE__ */ new Map();\nvar kTextScoreRange = 10;\nvar kExactPenalty = kTextScoreRange / 2;\nvar kTestIdScore = 1;\nvar kOtherTestIdScore = 2;\nvar kIframeByAttributeScore = 10;\nvar kBeginPenalizedScore = 50;\nvar kPlaceholderScore = 100;\nvar kLabelScore = 120;\nvar kRoleWithNameScore = 140;\nvar kAltTextScore = 160;\nvar kTextScore = 180;\nvar kTitleScore = 200;\nvar kTextScoreRegex = 250;\nvar kPlaceholderScoreExact = kPlaceholderScore + kExactPenalty;\nvar kLabelScoreExact = kLabelScore + kExactPenalty;\nvar kRoleWithNameScoreExact = kRoleWithNameScore + kExactPenalty;\nvar kAltTextScoreExact = kAltTextScore + kExactPenalty;\nvar kTextScoreExact = kTextScore + kExactPenalty;\nvar kTitleScoreExact = kTitleScore + kExactPenalty;\nvar kEndPenalizedScore = 300;\nvar kCSSIdScore = 500;\nvar kRoleWithoutNameScore = 510;\nvar kCSSInputTypeNameScore = 520;\nvar kCSSTagNameScore = 530;\nvar kNthScore = 1e4;\nvar kCSSFallbackScore = 1e7;\nfunction generateSelector(injectedScript, targetElement, options) {\n var _a;\n injectedScript._evaluator.begin();\n beginAriaCaches();\n try {\n targetElement = closestCrossShadow(targetElement, \"button,select,input,[role=button],[role=checkbox],[role=radio],a,[role=link]\", options.root) || targetElement;\n const targetTokens = generateSelectorFor(injectedScript, targetElement, options);\n const selector = joinTokens(targetTokens);\n const parsedSelector = injectedScript.parseSelector(selector);\n return {\n selector,\n elements: injectedScript.querySelectorAll(parsedSelector, (_a = options.root) != null ? _a : targetElement.ownerDocument)\n };\n } finally {\n cacheAllowText.clear();\n cacheDisallowText.clear();\n endAriaCaches();\n injectedScript._evaluator.end();\n }\n}\nfunction filterRegexTokens(textCandidates) {\n return textCandidates.filter((c) => c[0].selector[0] !== \"/\");\n}\nfunction generateSelectorFor(injectedScript, targetElement, options) {\n if (options.root && !isInsideScope(options.root, targetElement))\n throw new Error(`Target element must belong to the root's subtree`);\n if (targetElement === options.root)\n return [{ engine: \"css\", selector: \":scope\", score: 1 }];\n if (targetElement.ownerDocument.documentElement === targetElement)\n return [{ engine: \"css\", selector: \"html\", score: 1 }];\n const calculate = (element, allowText) => {\n var _a;\n const allowNthMatch = element === targetElement;\n let textCandidates = allowText ? buildTextCandidates(injectedScript, element, element === targetElement) : [];\n if (element !== targetElement) {\n textCandidates = filterRegexTokens(textCandidates);\n }\n const noTextCandidates = buildNoTextCandidates(injectedScript, element, options).filter((token) => !options.omitInternalEngines || !token.engine.startsWith(\"internal:\")).map((token) => [token]);\n let result = chooseFirstSelector(injectedScript, (_a = options.root) != null ? _a : targetElement.ownerDocument, element, [...textCandidates, ...noTextCandidates], allowNthMatch);\n textCandidates = filterRegexTokens(textCandidates);\n const checkWithText = (textCandidatesToUse) => {\n const allowParentText = allowText && !textCandidatesToUse.length;\n const candidates = [...textCandidatesToUse, ...noTextCandidates].filter((c) => {\n if (!result)\n return true;\n return combineScores(c) < combineScores(result);\n });\n let bestPossibleInParent = candidates[0];\n if (!bestPossibleInParent)\n return;\n for (let parent = parentElementOrShadowHost(element); parent && parent !== options.root; parent = parentElementOrShadowHost(parent)) {\n const parentTokens = calculateCached(parent, allowParentText);\n if (!parentTokens)\n continue;\n if (result && combineScores([...parentTokens, ...bestPossibleInParent]) >= combineScores(result))\n continue;\n bestPossibleInParent = chooseFirstSelector(injectedScript, parent, element, candidates, allowNthMatch);\n if (!bestPossibleInParent)\n return;\n const combined = [...parentTokens, ...bestPossibleInParent];\n if (!result || combineScores(combined) < combineScores(result))\n result = combined;\n }\n };\n checkWithText(textCandidates);\n if (element === targetElement && textCandidates.length)\n checkWithText([]);\n return result;\n };\n const calculateCached = (element, allowText) => {\n const cache = allowText ? cacheAllowText : cacheDisallowText;\n let value = cache.get(element);\n if (value === void 0) {\n value = calculate(element, allowText);\n cache.set(element, value);\n }\n return value;\n };\n return calculateCached(targetElement, true) || cssFallback(injectedScript, targetElement, options);\n}\nfunction buildNoTextCandidates(injectedScript, element, options) {\n const candidates = [];\n {\n for (const attr of [\"data-testid\", \"data-test-id\", \"data-test\"]) {\n if (attr !== options.testIdAttributeName && element.getAttribute(attr))\n candidates.push({ engine: \"css\", selector: `[${attr}=${quoteAttributeValue(element.getAttribute(attr))}]`, score: kOtherTestIdScore });\n }\n const idAttr = element.getAttribute(\"id\");\n if (idAttr && !isGuidLike(idAttr))\n candidates.push({ engine: \"css\", selector: makeSelectorForId(idAttr), score: kCSSIdScore });\n candidates.push({ engine: \"css\", selector: cssEscape(element.nodeName.toLowerCase()), score: kCSSTagNameScore });\n }\n if (element.nodeName === \"IFRAME\") {\n for (const attribute of [\"name\", \"title\"]) {\n if (element.getAttribute(attribute))\n candidates.push({ engine: \"css\", selector: `${cssEscape(element.nodeName.toLowerCase())}[${attribute}=${quoteAttributeValue(element.getAttribute(attribute))}]`, score: kIframeByAttributeScore });\n }\n if (element.getAttribute(options.testIdAttributeName))\n candidates.push({ engine: \"css\", selector: `[${options.testIdAttributeName}=${quoteAttributeValue(element.getAttribute(options.testIdAttributeName))}]`, score: kTestIdScore });\n penalizeScoreForLength([candidates]);\n return candidates;\n }\n if (element.getAttribute(options.testIdAttributeName))\n candidates.push({ engine: \"internal:testid\", selector: `[${options.testIdAttributeName}=${escapeForAttributeSelector(element.getAttribute(options.testIdAttributeName), true)}]`, score: kTestIdScore });\n if (element.nodeName === \"INPUT\" || element.nodeName === \"TEXTAREA\") {\n const input = element;\n if (input.placeholder) {\n candidates.push({ engine: \"internal:attr\", selector: `[placeholder=${escapeForAttributeSelector(input.placeholder, false)}]`, score: kPlaceholderScore });\n candidates.push({ engine: \"internal:attr\", selector: `[placeholder=${escapeForAttributeSelector(input.placeholder, true)}]`, score: kPlaceholderScoreExact });\n }\n }\n const labels = getElementLabels(injectedScript._evaluator._cacheText, element);\n for (const label of labels) {\n const labelText = label.full.trim();\n candidates.push({ engine: \"internal:label\", selector: escapeForTextSelector(labelText, false), score: kLabelScore });\n candidates.push({ engine: \"internal:label\", selector: escapeForTextSelector(labelText, true), score: kLabelScoreExact });\n }\n const ariaRole = getAriaRole(element);\n if (ariaRole && ![\"none\", \"presentation\"].includes(ariaRole))\n candidates.push({ engine: \"internal:role\", selector: ariaRole, score: kRoleWithoutNameScore });\n if (element.getAttribute(\"alt\") && [\"APPLET\", \"AREA\", \"IMG\", \"INPUT\"].includes(element.nodeName)) {\n candidates.push({ engine: \"internal:attr\", selector: `[alt=${escapeForAttributeSelector(element.getAttribute(\"alt\"), false)}]`, score: kAltTextScore });\n candidates.push({ engine: \"internal:attr\", selector: `[alt=${escapeForAttributeSelector(element.getAttribute(\"alt\"), true)}]`, score: kAltTextScoreExact });\n }\n if (element.getAttribute(\"name\") && [\"BUTTON\", \"FORM\", \"FIELDSET\", \"FRAME\", \"IFRAME\", \"INPUT\", \"KEYGEN\", \"OBJECT\", \"OUTPUT\", \"SELECT\", \"TEXTAREA\", \"MAP\", \"META\", \"PARAM\"].includes(element.nodeName))\n candidates.push({ engine: \"css\", selector: `${cssEscape(element.nodeName.toLowerCase())}[name=${quoteAttributeValue(element.getAttribute(\"name\"))}]`, score: kCSSInputTypeNameScore });\n if (element.getAttribute(\"title\")) {\n candidates.push({ engine: \"internal:attr\", selector: `[title=${escapeForAttributeSelector(element.getAttribute(\"title\"), false)}]`, score: kTitleScore });\n candidates.push({ engine: \"internal:attr\", selector: `[title=${escapeForAttributeSelector(element.getAttribute(\"title\"), true)}]`, score: kTitleScoreExact });\n }\n if ([\"INPUT\", \"TEXTAREA\"].includes(element.nodeName) && element.getAttribute(\"type\") !== \"hidden\") {\n if (element.getAttribute(\"type\"))\n candidates.push({ engine: \"css\", selector: `${cssEscape(element.nodeName.toLowerCase())}[type=${quoteAttributeValue(element.getAttribute(\"type\"))}]`, score: kCSSInputTypeNameScore });\n }\n if ([\"INPUT\", \"TEXTAREA\", \"SELECT\"].includes(element.nodeName) && element.getAttribute(\"type\") !== \"hidden\")\n candidates.push({ engine: \"css\", selector: cssEscape(element.nodeName.toLowerCase()), score: kCSSInputTypeNameScore + 1 });\n penalizeScoreForLength([candidates]);\n return candidates;\n}\nfunction buildTextCandidates(injectedScript, element, isTargetNode) {\n if (element.nodeName === \"SELECT\")\n return [];\n const candidates = [];\n const fullText = normalizeWhiteSpace(elementText(injectedScript._evaluator._cacheText, element).full);\n const text = fullText.substring(0, 80);\n if (text) {\n const escaped = escapeForTextSelector(text, false);\n if (isTargetNode) {\n candidates.push([{ engine: \"internal:text\", selector: escaped, score: kTextScore }]);\n candidates.push([{ engine: \"internal:text\", selector: escapeForTextSelector(text, true), score: kTextScoreExact }]);\n }\n const cssToken = { engine: \"css\", selector: element.nodeName.toLowerCase(), score: kCSSTagNameScore };\n candidates.push([cssToken, { engine: \"internal:has-text\", selector: escaped, score: kTextScore }]);\n if (fullText.length <= 80)\n candidates.push([cssToken, { engine: \"internal:has-text\", selector: \"/^\" + escapeRegExp(fullText) + \"$/\", score: kTextScoreRegex }]);\n }\n const ariaRole = getAriaRole(element);\n if (ariaRole && ![\"none\", \"presentation\"].includes(ariaRole)) {\n const ariaName = getElementAccessibleName(element, false);\n if (ariaName) {\n candidates.push([{ engine: \"internal:role\", selector: `${ariaRole}[name=${escapeForAttributeSelector(ariaName, false)}]`, score: kRoleWithNameScore }]);\n candidates.push([{ engine: \"internal:role\", selector: `${ariaRole}[name=${escapeForAttributeSelector(ariaName, true)}]`, score: kRoleWithNameScoreExact }]);\n }\n }\n penalizeScoreForLength(candidates);\n return candidates;\n}\nfunction makeSelectorForId(id) {\n return /^[a-zA-Z][a-zA-Z0-9\\-\\_]+$/.test(id) ? \"#\" + id : `[id=\"${cssEscape(id)}\"]`;\n}\nfunction cssFallback(injectedScript, targetElement, options) {\n var _a;\n const root = (_a = options.root) != null ? _a : targetElement.ownerDocument;\n const tokens = [];\n function uniqueCSSSelector(prefix) {\n const path = tokens.slice();\n if (prefix)\n path.unshift(prefix);\n const selector = path.join(\" > \");\n const parsedSelector = injectedScript.parseSelector(selector);\n const node = injectedScript.querySelector(parsedSelector, root, false);\n return node === targetElement ? selector : void 0;\n }\n function makeStrict(selector) {\n const token = { engine: \"css\", selector, score: kCSSFallbackScore };\n const parsedSelector = injectedScript.parseSelector(selector);\n const elements = injectedScript.querySelectorAll(parsedSelector, root);\n if (elements.length === 1)\n return [token];\n const nth = { engine: \"nth\", selector: String(elements.indexOf(targetElement)), score: kNthScore };\n return [token, nth];\n }\n for (let element = targetElement; element && element !== root; element = parentElementOrShadowHost(element)) {\n const nodeName = element.nodeName.toLowerCase();\n let bestTokenForLevel = \"\";\n if (element.id) {\n const token = makeSelectorForId(element.id);\n const selector = uniqueCSSSelector(token);\n if (selector)\n return makeStrict(selector);\n bestTokenForLevel = token;\n }\n const parent = element.parentNode;\n const classes = [...element.classList];\n for (let i = 0; i < classes.length; ++i) {\n const token = \".\" + cssEscape(classes.slice(0, i + 1).join(\".\"));\n const selector = uniqueCSSSelector(token);\n if (selector)\n return makeStrict(selector);\n if (!bestTokenForLevel && parent) {\n const sameClassSiblings = parent.querySelectorAll(token);\n if (sameClassSiblings.length === 1)\n bestTokenForLevel = token;\n }\n }\n if (parent) {\n const siblings = [...parent.children];\n const sameTagSiblings = siblings.filter((sibling) => sibling.nodeName.toLowerCase() === nodeName);\n const token = sameTagSiblings.indexOf(element) === 0 ? cssEscape(nodeName) : `${cssEscape(nodeName)}:nth-child(${1 + siblings.indexOf(element)})`;\n const selector = uniqueCSSSelector(token);\n if (selector)\n return makeStrict(selector);\n if (!bestTokenForLevel)\n bestTokenForLevel = token;\n } else if (!bestTokenForLevel) {\n bestTokenForLevel = nodeName;\n }\n tokens.unshift(bestTokenForLevel);\n }\n return makeStrict(uniqueCSSSelector());\n}\nfunction quoteAttributeValue(text) {\n return `\"${cssEscape(text).replace(/\\\\ /g, \" \")}\"`;\n}\nfunction penalizeScoreForLength(groups) {\n for (const group of groups) {\n for (const token of group) {\n if (token.score > kBeginPenalizedScore && token.score < kEndPenalizedScore)\n token.score += Math.min(kTextScoreRange, token.selector.length / 10 | 0);\n }\n }\n}\nfunction joinTokens(tokens) {\n const parts = [];\n let lastEngine = \"\";\n for (const { engine, selector } of tokens) {\n if (parts.length && (lastEngine !== \"css\" || engine !== \"css\" || selector.startsWith(\":nth-match(\")))\n parts.push(\">>\");\n lastEngine = engine;\n if (engine === \"css\")\n parts.push(selector);\n else\n parts.push(`${engine}=${selector}`);\n }\n return parts.join(\" \");\n}\nfunction combineScores(tokens) {\n let score = 0;\n for (let i = 0; i < tokens.length; i++)\n score += tokens[i].score * (tokens.length - i);\n return score;\n}\nfunction chooseFirstSelector(injectedScript, scope, targetElement, selectors, allowNthMatch) {\n const joined = selectors.map((tokens) => ({ tokens, score: combineScores(tokens) }));\n joined.sort((a, b) => a.score - b.score);\n let bestWithIndex = null;\n for (const { tokens } of joined) {\n const parsedSelector = injectedScript.parseSelector(joinTokens(tokens));\n const result = injectedScript.querySelectorAll(parsedSelector, scope);\n if (result[0] === targetElement && result.length === 1) {\n return tokens;\n }\n const index = result.indexOf(targetElement);\n if (!allowNthMatch || bestWithIndex || index === -1 || result.length > 5)\n continue;\n const nth = { engine: \"nth\", selector: String(index), score: kNthScore };\n bestWithIndex = [...tokens, nth];\n }\n return bestWithIndex;\n}\nfunction isGuidLike(id) {\n let lastCharacterType;\n let transitionCount = 0;\n for (let i = 0; i < id.length; ++i) {\n const c = id[i];\n let characterType;\n if (c === \"-\" || c === \"_\")\n continue;\n if (c >= \"a\" && c <= \"z\")\n characterType = \"lower\";\n else if (c >= \"A\" && c <= \"Z\")\n characterType = \"upper\";\n else if (c >= \"0\" && c <= \"9\")\n characterType = \"digit\";\n else\n characterType = \"other\";\n if (characterType === \"lower\" && lastCharacterType === \"upper\") {\n lastCharacterType = characterType;\n continue;\n }\n if (lastCharacterType && lastCharacterType !== characterType)\n ++transitionCount;\n lastCharacterType = characterType;\n }\n return transitionCount >= id.length / 4;\n}\nfunction escapeRegExp(s) {\n return s.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n\n// packages/playwright-core/src/utils/isomorphic/locatorGenerators.ts\nfunction asLocator(lang, selector, isFrameLocator = false, playSafe = false) {\n return asLocators(lang, selector, isFrameLocator, playSafe)[0];\n}\nfunction asLocators(lang, selector, isFrameLocator = false, playSafe = false, maxOutputSize = 20) {\n if (playSafe) {\n try {\n return innerAsLocators(generators[lang], parseSelector(selector), isFrameLocator, maxOutputSize);\n } catch (e) {\n return [selector];\n }\n } else {\n return innerAsLocators(generators[lang], parseSelector(selector), isFrameLocator, maxOutputSize);\n }\n}\nfunction innerAsLocators(factory, parsed, isFrameLocator = false, maxOutputSize = 20) {\n const parts = [...parsed.parts];\n for (let index = 0; index < parts.length - 1; index++) {\n if (parts[index].name === \"nth\" && parts[index + 1].name === \"internal:control\" && parts[index + 1].body === \"enter-frame\") {\n const [nth] = parts.splice(index, 1);\n parts.splice(index + 1, 0, nth);\n }\n }\n const tokens = [];\n let nextBase = isFrameLocator ? \"frame-locator\" : \"page\";\n for (let index = 0; index < parts.length; index++) {\n const part = parts[index];\n const base = nextBase;\n nextBase = \"locator\";\n if (part.name === \"nth\") {\n if (part.body === \"0\")\n tokens.push([factory.generateLocator(base, \"first\", \"\"), factory.generateLocator(base, \"nth\", \"0\")]);\n else if (part.body === \"-1\")\n tokens.push([factory.generateLocator(base, \"last\", \"\"), factory.generateLocator(base, \"nth\", \"-1\")]);\n else\n tokens.push([factory.generateLocator(base, \"nth\", part.body)]);\n continue;\n }\n if (part.name === \"internal:text\") {\n const { exact, text } = detectExact(part.body);\n tokens.push([factory.generateLocator(base, \"text\", text, { exact })]);\n continue;\n }\n if (part.name === \"internal:has-text\") {\n const { exact, text } = detectExact(part.body);\n if (!exact) {\n tokens.push([factory.generateLocator(base, \"has-text\", text, { exact })]);\n continue;\n }\n }\n if (part.name === \"internal:has-not-text\") {\n const { exact, text } = detectExact(part.body);\n if (!exact) {\n tokens.push([factory.generateLocator(base, \"has-not-text\", text, { exact })]);\n continue;\n }\n }\n if (part.name === \"internal:has\") {\n const inners = innerAsLocators(factory, part.body.parsed, false, maxOutputSize);\n tokens.push(inners.map((inner) => factory.generateLocator(base, \"has\", inner)));\n continue;\n }\n if (part.name === \"internal:has-not\") {\n const inners = innerAsLocators(factory, part.body.parsed, false, maxOutputSize);\n tokens.push(inners.map((inner) => factory.generateLocator(base, \"hasNot\", inner)));\n continue;\n }\n if (part.name === \"internal:and\") {\n const inners = innerAsLocators(factory, part.body.parsed, false, maxOutputSize);\n tokens.push(inners.map((inner) => factory.generateLocator(base, \"and\", inner)));\n continue;\n }\n if (part.name === \"internal:or\") {\n const inners = innerAsLocators(factory, part.body.parsed, false, maxOutputSize);\n tokens.push(inners.map((inner) => factory.generateLocator(base, \"or\", inner)));\n continue;\n }\n if (part.name === \"internal:label\") {\n const { exact, text } = detectExact(part.body);\n tokens.push([factory.generateLocator(base, \"label\", text, { exact })]);\n continue;\n }\n if (part.name === \"internal:role\") {\n const attrSelector = parseAttributeSelector(part.body, true);\n const options = { attrs: [] };\n for (const attr of attrSelector.attributes) {\n if (attr.name === \"name\") {\n options.exact = attr.caseSensitive;\n options.name = attr.value;\n } else {\n if (attr.name === \"level\" && typeof attr.value === \"string\")\n attr.value = +attr.value;\n options.attrs.push({ name: attr.name === \"include-hidden\" ? \"includeHidden\" : attr.name, value: attr.value });\n }\n }\n tokens.push([factory.generateLocator(base, \"role\", attrSelector.name, options)]);\n continue;\n }\n if (part.name === \"internal:testid\") {\n const attrSelector = parseAttributeSelector(part.body, true);\n const { value } = attrSelector.attributes[0];\n tokens.push([factory.generateLocator(base, \"test-id\", value)]);\n continue;\n }\n if (part.name === \"internal:attr\") {\n const attrSelector = parseAttributeSelector(part.body, true);\n const { name, value, caseSensitive } = attrSelector.attributes[0];\n const text = value;\n const exact = !!caseSensitive;\n if (name === \"placeholder\") {\n tokens.push([factory.generateLocator(base, \"placeholder\", text, { exact })]);\n continue;\n }\n if (name === \"alt\") {\n tokens.push([factory.generateLocator(base, \"alt\", text, { exact })]);\n continue;\n }\n if (name === \"title\") {\n tokens.push([factory.generateLocator(base, \"title\", text, { exact })]);\n continue;\n }\n }\n let locatorType = \"default\";\n const nextPart = parts[index + 1];\n if (nextPart && nextPart.name === \"internal:control\" && nextPart.body === \"enter-frame\") {\n locatorType = \"frame\";\n nextBase = \"frame-locator\";\n index++;\n }\n const selectorPart = stringifySelector({ parts: [part] });\n const locatorPart = factory.generateLocator(base, locatorType, selectorPart);\n if (locatorType === \"default\" && nextPart && [\"internal:has-text\", \"internal:has-not-text\"].includes(nextPart.name)) {\n const { exact, text } = detectExact(nextPart.body);\n if (!exact) {\n const nextLocatorPart = factory.generateLocator(\"locator\", nextPart.name === \"internal:has-text\" ? \"has-text\" : \"has-not-text\", text, { exact });\n const options = {};\n if (nextPart.name === \"internal:has-text\")\n options.hasText = text;\n else\n options.hasNotText = text;\n const combinedPart = factory.generateLocator(base, \"default\", selectorPart, options);\n tokens.push([factory.chainLocators([locatorPart, nextLocatorPart]), combinedPart]);\n index++;\n continue;\n }\n }\n tokens.push([locatorPart]);\n }\n return combineTokens(factory, tokens, maxOutputSize);\n}\nfunction combineTokens(factory, tokens, maxOutputSize) {\n const currentTokens = tokens.map(() => \"\");\n const result = [];\n const visit = (index) => {\n if (index === tokens.length) {\n result.push(factory.chainLocators(currentTokens));\n return currentTokens.length < maxOutputSize;\n }\n for (const taken of tokens[index]) {\n currentTokens[index] = taken;\n if (!visit(index + 1))\n return false;\n }\n return true;\n };\n visit(0);\n return result;\n}\nfunction detectExact(text) {\n let exact = false;\n const match = text.match(/^\\/(.*)\\/([igm]*)$/);\n if (match)\n return { text: new RegExp(match[1], match[2]) };\n if (text.endsWith('\"')) {\n text = JSON.parse(text);\n exact = true;\n } else if (text.endsWith('\"s')) {\n text = JSON.parse(text.substring(0, text.length - 1));\n exact = true;\n } else if (text.endsWith('\"i')) {\n text = JSON.parse(text.substring(0, text.length - 1));\n exact = false;\n }\n return { exact, text };\n}\nvar JavaScriptLocatorFactory = class {\n generateLocator(base, kind, body, options = {}) {\n switch (kind) {\n case \"default\":\n if (options.hasText !== void 0)\n return `locator(${this.quote(body)}, { hasText: ${this.toHasText(options.hasText)} })`;\n if (options.hasNotText !== void 0)\n return `locator(${this.quote(body)}, { hasNotText: ${this.toHasText(options.hasNotText)} })`;\n return `locator(${this.quote(body)})`;\n case \"frame\":\n return `frameLocator(${this.quote(body)})`;\n case \"nth\":\n return `nth(${body})`;\n case \"first\":\n return `first()`;\n case \"last\":\n return `last()`;\n case \"role\":\n const attrs = [];\n if (isRegExp(options.name)) {\n attrs.push(`name: ${options.name}`);\n } else if (typeof options.name === \"string\") {\n attrs.push(`name: ${this.quote(options.name)}`);\n if (options.exact)\n attrs.push(`exact: true`);\n }\n for (const { name, value } of options.attrs)\n attrs.push(`${name}: ${typeof value === \"string\" ? this.quote(value) : value}`);\n const attrString = attrs.length ? `, { ${attrs.join(\", \")} }` : \"\";\n return `getByRole(${this.quote(body)}${attrString})`;\n case \"has-text\":\n return `filter({ hasText: ${this.toHasText(body)} })`;\n case \"has-not-text\":\n return `filter({ hasNotText: ${this.toHasText(body)} })`;\n case \"has\":\n return `filter({ has: ${body} })`;\n case \"hasNot\":\n return `filter({ hasNot: ${body} })`;\n case \"and\":\n return `and(${body})`;\n case \"or\":\n return `or(${body})`;\n case \"test-id\":\n return `getByTestId(${this.toTestIdValue(body)})`;\n case \"text\":\n return this.toCallWithExact(\"getByText\", body, !!options.exact);\n case \"alt\":\n return this.toCallWithExact(\"getByAltText\", body, !!options.exact);\n case \"placeholder\":\n return this.toCallWithExact(\"getByPlaceholder\", body, !!options.exact);\n case \"label\":\n return this.toCallWithExact(\"getByLabel\", body, !!options.exact);\n case \"title\":\n return this.toCallWithExact(\"getByTitle\", body, !!options.exact);\n default:\n throw new Error(\"Unknown selector kind \" + kind);\n }\n }\n chainLocators(locators) {\n return locators.join(\".\");\n }\n toCallWithExact(method, body, exact) {\n if (isRegExp(body))\n return `${method}(${body})`;\n return exact ? `${method}(${this.quote(body)}, { exact: true })` : `${method}(${this.quote(body)})`;\n }\n toHasText(body) {\n if (isRegExp(body))\n return String(body);\n return this.quote(body);\n }\n toTestIdValue(value) {\n if (isRegExp(value))\n return String(value);\n return this.quote(value);\n }\n quote(text) {\n return escapeWithQuotes(text, \"'\");\n }\n};\nvar PythonLocatorFactory = class {\n generateLocator(base, kind, body, options = {}) {\n switch (kind) {\n case \"default\":\n if (options.hasText !== void 0)\n return `locator(${this.quote(body)}, has_text=${this.toHasText(options.hasText)})`;\n if (options.hasNotText !== void 0)\n return `locator(${this.quote(body)}, has_not_text=${this.toHasText(options.hasNotText)})`;\n return `locator(${this.quote(body)})`;\n case \"frame\":\n return `frame_locator(${this.quote(body)})`;\n case \"nth\":\n return `nth(${body})`;\n case \"first\":\n return `first`;\n case \"last\":\n return `last`;\n case \"role\":\n const attrs = [];\n if (isRegExp(options.name)) {\n attrs.push(`name=${this.regexToString(options.name)}`);\n } else if (typeof options.name === \"string\") {\n attrs.push(`name=${this.quote(options.name)}`);\n if (options.exact)\n attrs.push(`exact=True`);\n }\n for (const { name, value } of options.attrs) {\n let valueString = typeof value === \"string\" ? this.quote(value) : value;\n if (typeof value === \"boolean\")\n valueString = value ? \"True\" : \"False\";\n attrs.push(`${toSnakeCase(name)}=${valueString}`);\n }\n const attrString = attrs.length ? `, ${attrs.join(\", \")}` : \"\";\n return `get_by_role(${this.quote(body)}${attrString})`;\n case \"has-text\":\n return `filter(has_text=${this.toHasText(body)})`;\n case \"has-not-text\":\n return `filter(has_not_text=${this.toHasText(body)})`;\n case \"has\":\n return `filter(has=${body})`;\n case \"hasNot\":\n return `filter(has_not=${body})`;\n case \"and\":\n return `and_(${body})`;\n case \"or\":\n return `or_(${body})`;\n case \"test-id\":\n return `get_by_test_id(${this.toTestIdValue(body)})`;\n case \"text\":\n return this.toCallWithExact(\"get_by_text\", body, !!options.exact);\n case \"alt\":\n return this.toCallWithExact(\"get_by_alt_text\", body, !!options.exact);\n case \"placeholder\":\n return this.toCallWithExact(\"get_by_placeholder\", body, !!options.exact);\n case \"label\":\n return this.toCallWithExact(\"get_by_label\", body, !!options.exact);\n case \"title\":\n return this.toCallWithExact(\"get_by_title\", body, !!options.exact);\n default:\n throw new Error(\"Unknown selector kind \" + kind);\n }\n }\n chainLocators(locators) {\n return locators.join(\".\");\n }\n regexToString(body) {\n const suffix = body.flags.includes(\"i\") ? \", re.IGNORECASE\" : \"\";\n return `re.compile(r\"${body.source.replace(/\\\\\\//, \"/\").replace(/\"/g, '\\\\\"')}\"${suffix})`;\n }\n toCallWithExact(method, body, exact) {\n if (isRegExp(body))\n return `${method}(${this.regexToString(body)})`;\n if (exact)\n return `${method}(${this.quote(body)}, exact=True)`;\n return `${method}(${this.quote(body)})`;\n }\n toHasText(body) {\n if (isRegExp(body))\n return this.regexToString(body);\n return `${this.quote(body)}`;\n }\n toTestIdValue(value) {\n if (isRegExp(value))\n return this.regexToString(value);\n return this.quote(value);\n }\n quote(text) {\n return escapeWithQuotes(text, '\"');\n }\n};\nvar JavaLocatorFactory = class {\n generateLocator(base, kind, body, options = {}) {\n let clazz;\n switch (base) {\n case \"page\":\n clazz = \"Page\";\n break;\n case \"frame-locator\":\n clazz = \"FrameLocator\";\n break;\n case \"locator\":\n clazz = \"Locator\";\n break;\n }\n switch (kind) {\n case \"default\":\n if (options.hasText !== void 0)\n return `locator(${this.quote(body)}, new ${clazz}.LocatorOptions().setHasText(${this.toHasText(options.hasText)}))`;\n if (options.hasNotText !== void 0)\n return `locator(${this.quote(body)}, new ${clazz}.LocatorOptions().setHasNotText(${this.toHasText(options.hasNotText)}))`;\n return `locator(${this.quote(body)})`;\n case \"frame\":\n return `frameLocator(${this.quote(body)})`;\n case \"nth\":\n return `nth(${body})`;\n case \"first\":\n return `first()`;\n case \"last\":\n return `last()`;\n case \"role\":\n const attrs = [];\n if (isRegExp(options.name)) {\n attrs.push(`.setName(${this.regexToString(options.name)})`);\n } else if (typeof options.name === \"string\") {\n attrs.push(`.setName(${this.quote(options.name)})`);\n if (options.exact)\n attrs.push(`.setExact(true)`);\n }\n for (const { name, value } of options.attrs)\n attrs.push(`.set${toTitleCase(name)}(${typeof value === \"string\" ? this.quote(value) : value})`);\n const attrString = attrs.length ? `, new ${clazz}.GetByRoleOptions()${attrs.join(\"\")}` : \"\";\n return `getByRole(AriaRole.${toSnakeCase(body).toUpperCase()}${attrString})`;\n case \"has-text\":\n return `filter(new ${clazz}.FilterOptions().setHasText(${this.toHasText(body)}))`;\n case \"has-not-text\":\n return `filter(new ${clazz}.FilterOptions().setHasNotText(${this.toHasText(body)}))`;\n case \"has\":\n return `filter(new ${clazz}.FilterOptions().setHas(${body}))`;\n case \"hasNot\":\n return `filter(new ${clazz}.FilterOptions().setHasNot(${body}))`;\n case \"and\":\n return `and(${body})`;\n case \"or\":\n return `or(${body})`;\n case \"test-id\":\n return `getByTestId(${this.toTestIdValue(body)})`;\n case \"text\":\n return this.toCallWithExact(clazz, \"getByText\", body, !!options.exact);\n case \"alt\":\n return this.toCallWithExact(clazz, \"getByAltText\", body, !!options.exact);\n case \"placeholder\":\n return this.toCallWithExact(clazz, \"getByPlaceholder\", body, !!options.exact);\n case \"label\":\n return this.toCallWithExact(clazz, \"getByLabel\", body, !!options.exact);\n case \"title\":\n return this.toCallWithExact(clazz, \"getByTitle\", body, !!options.exact);\n default:\n throw new Error(\"Unknown selector kind \" + kind);\n }\n }\n chainLocators(locators) {\n return locators.join(\".\");\n }\n regexToString(body) {\n const suffix = body.flags.includes(\"i\") ? \", Pattern.CASE_INSENSITIVE\" : \"\";\n return `Pattern.compile(${this.quote(body.source)}${suffix})`;\n }\n toCallWithExact(clazz, method, body, exact) {\n if (isRegExp(body))\n return `${method}(${this.regexToString(body)})`;\n if (exact)\n return `${method}(${this.quote(body)}, new ${clazz}.${toTitleCase(method)}Options().setExact(true))`;\n return `${method}(${this.quote(body)})`;\n }\n toHasText(body) {\n if (isRegExp(body))\n return this.regexToString(body);\n return this.quote(body);\n }\n toTestIdValue(value) {\n if (isRegExp(value))\n return this.regexToString(value);\n return this.quote(value);\n }\n quote(text) {\n return escapeWithQuotes(text, '\"');\n }\n};\nvar CSharpLocatorFactory = class {\n generateLocator(base, kind, body, options = {}) {\n switch (kind) {\n case \"default\":\n if (options.hasText !== void 0)\n return `Locator(${this.quote(body)}, new() { ${this.toHasText(options.hasText)} })`;\n if (options.hasNotText !== void 0)\n return `Locator(${this.quote(body)}, new() { ${this.toHasNotText(options.hasNotText)} })`;\n return `Locator(${this.quote(body)})`;\n case \"frame\":\n return `FrameLocator(${this.quote(body)})`;\n case \"nth\":\n return `Nth(${body})`;\n case \"first\":\n return `First`;\n case \"last\":\n return `Last`;\n case \"role\":\n const attrs = [];\n if (isRegExp(options.name)) {\n attrs.push(`NameRegex = ${this.regexToString(options.name)}`);\n } else if (typeof options.name === \"string\") {\n attrs.push(`Name = ${this.quote(options.name)}`);\n if (options.exact)\n attrs.push(`Exact = true`);\n }\n for (const { name, value } of options.attrs)\n attrs.push(`${toTitleCase(name)} = ${typeof value === \"string\" ? this.quote(value) : value}`);\n const attrString = attrs.length ? `, new() { ${attrs.join(\", \")} }` : \"\";\n return `GetByRole(AriaRole.${toTitleCase(body)}${attrString})`;\n case \"has-text\":\n return `Filter(new() { ${this.toHasText(body)} })`;\n case \"has-not-text\":\n return `Filter(new() { ${this.toHasNotText(body)} })`;\n case \"has\":\n return `Filter(new() { Has = ${body} })`;\n case \"hasNot\":\n return `Filter(new() { HasNot = ${body} })`;\n case \"and\":\n return `And(${body})`;\n case \"or\":\n return `Or(${body})`;\n case \"test-id\":\n return `GetByTestId(${this.toTestIdValue(body)})`;\n case \"text\":\n return this.toCallWithExact(\"GetByText\", body, !!options.exact);\n case \"alt\":\n return this.toCallWithExact(\"GetByAltText\", body, !!options.exact);\n case \"placeholder\":\n return this.toCallWithExact(\"GetByPlaceholder\", body, !!options.exact);\n case \"label\":\n return this.toCallWithExact(\"GetByLabel\", body, !!options.exact);\n case \"title\":\n return this.toCallWithExact(\"GetByTitle\", body, !!options.exact);\n default:\n throw new Error(\"Unknown selector kind \" + kind);\n }\n }\n chainLocators(locators) {\n return locators.join(\".\");\n }\n regexToString(body) {\n const suffix = body.flags.includes(\"i\") ? \", RegexOptions.IgnoreCase\" : \"\";\n return `new Regex(${this.quote(body.source)}${suffix})`;\n }\n toCallWithExact(method, body, exact) {\n if (isRegExp(body))\n return `${method}(${this.regexToString(body)})`;\n if (exact)\n return `${method}(${this.quote(body)}, new() { Exact = true })`;\n return `${method}(${this.quote(body)})`;\n }\n toHasText(body) {\n if (isRegExp(body))\n return `HasTextRegex = ${this.regexToString(body)}`;\n return `HasText = ${this.quote(body)}`;\n }\n toTestIdValue(value) {\n if (isRegExp(value))\n return this.regexToString(value);\n return this.quote(value);\n }\n toHasNotText(body) {\n if (isRegExp(body))\n return `HasNotTextRegex = ${this.regexToString(body)}`;\n return `HasNotText = ${this.quote(body)}`;\n }\n quote(text) {\n return escapeWithQuotes(text, '\"');\n }\n};\nvar JsonlLocatorFactory = class {\n generateLocator(base, kind, body, options = {}) {\n return JSON.stringify({\n kind,\n body,\n options\n });\n }\n chainLocators(locators) {\n const objects = locators.map((l) => JSON.parse(l));\n for (let i = 0; i < objects.length - 1; ++i)\n objects[i].next = objects[i + 1];\n return JSON.stringify(objects[0]);\n }\n};\nvar generators = {\n javascript: new JavaScriptLocatorFactory(),\n python: new PythonLocatorFactory(),\n java: new JavaLocatorFactory(),\n csharp: new CSharpLocatorFactory(),\n jsonl: new JsonlLocatorFactory()\n};\nfunction isRegExp(obj) {\n return obj instanceof RegExp;\n}\n\n// packages/playwright-core/src/server/injected/highlight.ts\nvar Highlight = class {\n constructor(injectedScript) {\n this._highlightEntries = [];\n this._language = \"javascript\";\n this._injectedScript = injectedScript;\n const document = injectedScript.document;\n this._isUnderTest = injectedScript.isUnderTest;\n this._glassPaneElement = document.createElement(\"x-pw-glass\");\n this._glassPaneElement.style.position = \"fixed\";\n this._glassPaneElement.style.top = \"0\";\n this._glassPaneElement.style.right = \"0\";\n this._glassPaneElement.style.bottom = \"0\";\n this._glassPaneElement.style.left = \"0\";\n this._glassPaneElement.style.zIndex = \"2147483647\";\n this._glassPaneElement.style.pointerEvents = \"none\";\n this._glassPaneElement.style.display = \"flex\";\n this._glassPaneElement.style.backgroundColor = \"transparent\";\n this._actionPointElement = document.createElement(\"x-pw-action-point\");\n this._actionPointElement.setAttribute(\"hidden\", \"true\");\n this._glassPaneShadow = this._glassPaneElement.attachShadow({ mode: this._isUnderTest ? \"open\" : \"closed\" });\n this._glassPaneShadow.appendChild(this._actionPointElement);\n const styleElement = document.createElement(\"style\");\n styleElement.textContent = `\n x-pw-tooltip {\n align-items: center;\n backdrop-filter: blur(5px);\n background-color: rgba(0, 0, 0, 0.7);\n border-radius: 2px;\n box-shadow: rgba(0, 0, 0, 0.1) 0px 3.6px 3.7px,\n rgba(0, 0, 0, 0.15) 0px 12.1px 12.3px,\n rgba(0, 0, 0, 0.1) 0px -2px 4px,\n rgba(0, 0, 0, 0.15) 0px -12.1px 24px,\n rgba(0, 0, 0, 0.25) 0px 54px 55px;\n color: rgb(204, 204, 204);\n display: none;\n font-family: 'Dank Mono', 'Operator Mono', Inconsolata, 'Fira Mono',\n 'SF Mono', Monaco, 'Droid Sans Mono', 'Source Code Pro', monospace;\n font-size: 12.8px;\n font-weight: normal;\n left: 0;\n line-height: 1.5;\n max-width: 600px;\n padding: 3.2px 5.12px 3.2px;\n position: absolute;\n top: 0;\n }\n x-pw-action-point {\n position: absolute;\n width: 20px;\n height: 20px;\n background: red;\n border-radius: 10px;\n pointer-events: none;\n margin: -10px 0 0 -10px;\n z-index: 2;\n }\n *[hidden] {\n display: none !important;\n }\n `;\n this._glassPaneShadow.appendChild(styleElement);\n }\n install() {\n this._injectedScript.document.documentElement.appendChild(this._glassPaneElement);\n }\n setLanguage(language) {\n this._language = language;\n }\n runHighlightOnRaf(selector) {\n if (this._rafRequest)\n cancelAnimationFrame(this._rafRequest);\n this.updateHighlight(this._injectedScript.querySelectorAll(selector, this._injectedScript.document.documentElement), stringifySelector(selector), false);\n this._rafRequest = requestAnimationFrame(() => this.runHighlightOnRaf(selector));\n }\n uninstall() {\n if (this._rafRequest)\n cancelAnimationFrame(this._rafRequest);\n this._glassPaneElement.remove();\n }\n isInstalled() {\n return this._glassPaneElement.parentElement === this._injectedScript.document.documentElement && !this._glassPaneElement.nextElementSibling;\n }\n showActionPoint(x, y) {\n this._actionPointElement.style.top = y + \"px\";\n this._actionPointElement.style.left = x + \"px\";\n this._actionPointElement.hidden = false;\n if (this._isUnderTest)\n console.error(\"Action point for test: \" + JSON.stringify({ x, y }));\n }\n hideActionPoint() {\n this._actionPointElement.hidden = true;\n }\n clearHighlight() {\n var _a, _b;\n for (const entry of this._highlightEntries) {\n (_a = entry.highlightElement) == null ? void 0 : _a.remove();\n (_b = entry.tooltipElement) == null ? void 0 : _b.remove();\n }\n this._highlightEntries = [];\n }\n updateHighlight(elements, selector, isRecording) {\n let color;\n if (isRecording)\n color = \"#dc6f6f7f\";\n else\n color = elements.length > 1 ? \"#f6b26b7f\" : \"#6fa8dc7f\";\n this._innerUpdateHighlight(elements, { color, tooltipText: selector ? asLocator(this._language, selector) : \"\" });\n }\n maskElements(elements, color) {\n this._innerUpdateHighlight(elements, { color: color ? color : \"#F0F\" });\n }\n _innerUpdateHighlight(elements, options) {\n if (this._highlightIsUpToDate(elements, options.tooltipText))\n return;\n this.clearHighlight();\n for (let i = 0; i < elements.length; ++i) {\n const highlightElement = this._createHighlightElement();\n this._glassPaneShadow.appendChild(highlightElement);\n let tooltipElement;\n if (options.tooltipText) {\n tooltipElement = this._injectedScript.document.createElement(\"x-pw-tooltip\");\n this._glassPaneShadow.appendChild(tooltipElement);\n const suffix = elements.length > 1 ? ` [${i + 1} of ${elements.length}]` : \"\";\n tooltipElement.textContent = options.tooltipText + suffix;\n tooltipElement.style.top = \"0\";\n tooltipElement.style.left = \"0\";\n tooltipElement.style.display = \"flex\";\n }\n this._highlightEntries.push({ targetElement: elements[i], tooltipElement, highlightElement, tooltipText: options.tooltipText });\n }\n for (const entry of this._highlightEntries) {\n entry.box = entry.targetElement.getBoundingClientRect();\n if (!entry.tooltipElement)\n continue;\n const tooltipWidth = entry.tooltipElement.offsetWidth;\n const tooltipHeight = entry.tooltipElement.offsetHeight;\n const totalWidth = this._glassPaneElement.offsetWidth;\n const totalHeight = this._glassPaneElement.offsetHeight;\n let anchorLeft = entry.box.left;\n if (anchorLeft + tooltipWidth > totalWidth - 5)\n anchorLeft = totalWidth - tooltipWidth - 5;\n let anchorTop = entry.box.bottom + 5;\n if (anchorTop + tooltipHeight > totalHeight - 5) {\n if (entry.box.top > tooltipHeight + 5) {\n anchorTop = entry.box.top - tooltipHeight - 5;\n } else {\n anchorTop = totalHeight - 5 - tooltipHeight;\n }\n }\n entry.tooltipTop = anchorTop;\n entry.tooltipLeft = anchorLeft;\n }\n for (const entry of this._highlightEntries) {\n if (entry.tooltipElement) {\n entry.tooltipElement.style.top = entry.tooltipTop + \"px\";\n entry.tooltipElement.style.left = entry.tooltipLeft + \"px\";\n }\n const box = entry.box;\n entry.highlightElement.style.backgroundColor = options.color;\n entry.highlightElement.style.left = box.x + \"px\";\n entry.highlightElement.style.top = box.y + \"px\";\n entry.highlightElement.style.width = box.width + \"px\";\n entry.highlightElement.style.height = box.height + \"px\";\n entry.highlightElement.style.display = \"block\";\n if (this._isUnderTest)\n console.error(\"Highlight box for test: \" + JSON.stringify({ x: box.x, y: box.y, width: box.width, height: box.height }));\n }\n }\n _highlightIsUpToDate(elements, tooltipText) {\n if (elements.length !== this._highlightEntries.length)\n return false;\n for (let i = 0; i < this._highlightEntries.length; ++i) {\n if (tooltipText !== this._highlightEntries[i].tooltipText)\n return false;\n if (elements[i] !== this._highlightEntries[i].targetElement)\n return false;\n const oldBox = this._highlightEntries[i].box;\n if (!oldBox)\n return false;\n const box = elements[i].getBoundingClientRect();\n if (box.top !== oldBox.top || box.right !== oldBox.right || box.bottom !== oldBox.bottom || box.left !== oldBox.left)\n return false;\n }\n return true;\n }\n _createHighlightElement() {\n const highlightElement = this._injectedScript.document.createElement(\"x-pw-highlight\");\n highlightElement.style.position = \"absolute\";\n highlightElement.style.top = \"0\";\n highlightElement.style.left = \"0\";\n highlightElement.style.width = \"0\";\n highlightElement.style.height = \"0\";\n highlightElement.style.boxSizing = \"border-box\";\n return highlightElement;\n }\n};\n\n// packages/playwright-core/src/server/injected/injectedScript.ts\nvar InjectedScript = class {\n constructor(window, isUnderTest, sdkLanguage, testIdAttributeNameForStrictErrorAndConsoleCodegen, stableRafCount, browserName, customEngines) {\n this.onGlobalListenersRemoved = /* @__PURE__ */ new Set();\n this._testIdAttributeNameForStrictErrorAndConsoleCodegen = \"data-testid\";\n this.window = window;\n this.document = window.document;\n this.isUnderTest = isUnderTest;\n this._sdkLanguage = sdkLanguage;\n this._testIdAttributeNameForStrictErrorAndConsoleCodegen = testIdAttributeNameForStrictErrorAndConsoleCodegen;\n this._evaluator = new SelectorEvaluatorImpl(/* @__PURE__ */ new Map());\n this._engines = /* @__PURE__ */ new Map();\n this._engines.set(\"xpath\", XPathEngine);\n this._engines.set(\"xpath:light\", XPathEngine);\n this._engines.set(\"_react\", ReactEngine);\n this._engines.set(\"_vue\", VueEngine);\n this._engines.set(\"role\", createRoleEngine(false));\n this._engines.set(\"text\", this._createTextEngine(true, false));\n this._engines.set(\"text:light\", this._createTextEngine(false, false));\n this._engines.set(\"id\", this._createAttributeEngine(\"id\", true));\n this._engines.set(\"id:light\", this._createAttributeEngine(\"id\", false));\n this._engines.set(\"data-testid\", this._createAttributeEngine(\"data-testid\", true));\n this._engines.set(\"data-testid:light\", this._createAttributeEngine(\"data-testid\", false));\n this._engines.set(\"data-test-id\", this._createAttributeEngine(\"data-test-id\", true));\n this._engines.set(\"data-test-id:light\", this._createAttributeEngine(\"data-test-id\", false));\n this._engines.set(\"data-test\", this._createAttributeEngine(\"data-test\", true));\n this._engines.set(\"data-test:light\", this._createAttributeEngine(\"data-test\", false));\n this._engines.set(\"css\", this._createCSSEngine());\n this._engines.set(\"nth\", { queryAll: () => [] });\n this._engines.set(\"visible\", this._createVisibleEngine());\n this._engines.set(\"internal:control\", this._createControlEngine());\n this._engines.set(\"internal:has\", this._createHasEngine());\n this._engines.set(\"internal:has-not\", this._createHasNotEngine());\n this._engines.set(\"internal:and\", { queryAll: () => [] });\n this._engines.set(\"internal:or\", { queryAll: () => [] });\n this._engines.set(\"internal:label\", this._createInternalLabelEngine());\n this._engines.set(\"internal:text\", this._createTextEngine(true, true));\n this._engines.set(\"internal:has-text\", this._createInternalHasTextEngine());\n this._engines.set(\"internal:has-not-text\", this._createInternalHasNotTextEngine());\n this._engines.set(\"internal:attr\", this._createNamedAttributeEngine());\n this._engines.set(\"internal:testid\", this._createNamedAttributeEngine());\n this._engines.set(\"internal:role\", createRoleEngine(true));\n for (const { name, engine } of customEngines)\n this._engines.set(name, engine);\n this._stableRafCount = stableRafCount;\n this._browserName = browserName;\n this._setupGlobalListenersRemovalDetection();\n this._setupHitTargetInterceptors();\n if (isUnderTest)\n this.window.__injectedScript = this;\n }\n eval(expression) {\n return this.window.eval(expression);\n }\n testIdAttributeNameForStrictErrorAndConsoleCodegen() {\n return this._testIdAttributeNameForStrictErrorAndConsoleCodegen;\n }\n parseSelector(selector) {\n const result = parseSelector(selector);\n visitAllSelectorParts(result, (part) => {\n if (!this._engines.has(part.name))\n throw this.createStacklessError(`Unknown engine \"${part.name}\" while parsing selector ${selector}`);\n });\n return result;\n }\n generateSelector(targetElement, options) {\n return generateSelector(this, targetElement, { ...options, testIdAttributeName: this._testIdAttributeNameForStrictErrorAndConsoleCodegen }).selector;\n }\n querySelector(selector, root, strict) {\n const result = this.querySelectorAll(selector, root);\n if (strict && result.length > 1)\n throw this.strictModeViolationError(selector, result);\n return result[0];\n }\n _queryNth(elements, part) {\n const list = [...elements];\n let nth = +part.body;\n if (nth === -1)\n nth = list.length - 1;\n return new Set(list.slice(nth, nth + 1));\n }\n _queryLayoutSelector(elements, part, originalRoot) {\n const name = part.name;\n const body = part.body;\n const result = [];\n const inner = this.querySelectorAll(body.parsed, originalRoot);\n for (const element of elements) {\n const score = layoutSelectorScore(name, element, inner, body.distance);\n if (score !== void 0)\n result.push({ element, score });\n }\n result.sort((a, b) => a.score - b.score);\n return new Set(result.map((r) => r.element));\n }\n querySelectorAll(selector, root) {\n if (selector.capture !== void 0) {\n if (selector.parts.some((part) => part.name === \"nth\"))\n throw this.createStacklessError(`Can't query n-th element in a request with the capture.`);\n const withHas = { parts: selector.parts.slice(0, selector.capture + 1) };\n if (selector.capture < selector.parts.length - 1) {\n const parsed = { parts: selector.parts.slice(selector.capture + 1) };\n const has = { name: \"internal:has\", body: { parsed }, source: stringifySelector(parsed) };\n withHas.parts.push(has);\n }\n return this.querySelectorAll(withHas, root);\n }\n if (!root[\"querySelectorAll\"])\n throw this.createStacklessError(\"Node is not queryable.\");\n if (selector.capture !== void 0) {\n throw this.createStacklessError(\"Internal error: there should not be a capture in the selector.\");\n }\n this._evaluator.begin();\n try {\n let roots = /* @__PURE__ */ new Set([root]);\n for (const part of selector.parts) {\n if (part.name === \"nth\") {\n roots = this._queryNth(roots, part);\n } else if (part.name === \"internal:and\") {\n const andElements = this.querySelectorAll(part.body.parsed, root);\n roots = new Set(andElements.filter((e) => roots.has(e)));\n } else if (part.name === \"internal:or\") {\n const orElements = this.querySelectorAll(part.body.parsed, root);\n roots = new Set(sortInDOMOrder(/* @__PURE__ */ new Set([...roots, ...orElements])));\n } else if (kLayoutSelectorNames.includes(part.name)) {\n roots = this._queryLayoutSelector(roots, part, root);\n } else {\n const next = /* @__PURE__ */ new Set();\n for (const root2 of roots) {\n const all = this._queryEngineAll(part, root2);\n for (const one of all)\n next.add(one);\n }\n roots = next;\n }\n }\n return [...roots];\n } finally {\n this._evaluator.end();\n }\n }\n _queryEngineAll(part, root) {\n const result = this._engines.get(part.name).queryAll(root, part.body);\n for (const element of result) {\n if (!(\"nodeName\" in element))\n throw this.createStacklessError(`Expected a Node but got ${Object.prototype.toString.call(element)}`);\n }\n return result;\n }\n _createAttributeEngine(attribute, shadow) {\n const toCSS = (selector) => {\n const css = `[${attribute}=${JSON.stringify(selector)}]`;\n return [{ simples: [{ selector: { css, functions: [] }, combinator: \"\" }] }];\n };\n return {\n queryAll: (root, selector) => {\n return this._evaluator.query({ scope: root, pierceShadow: shadow }, toCSS(selector));\n }\n };\n }\n _createCSSEngine() {\n return {\n queryAll: (root, body) => {\n return this._evaluator.query({ scope: root, pierceShadow: true }, body);\n }\n };\n }\n _createTextEngine(shadow, internal) {\n const queryAll = (root, selector) => {\n const { matcher, kind } = createTextMatcher(selector, internal);\n const result = [];\n let lastDidNotMatchSelf = null;\n const appendElement = (element) => {\n if (kind === \"lax\" && lastDidNotMatchSelf && lastDidNotMatchSelf.contains(element))\n return false;\n const matches = elementMatchesText(this._evaluator._cacheText, element, matcher);\n if (matches === \"none\")\n lastDidNotMatchSelf = element;\n if (matches === \"self\" || matches === \"selfAndChildren\" && kind === \"strict\" && !internal)\n result.push(element);\n };\n if (root.nodeType === Node.ELEMENT_NODE)\n appendElement(root);\n const elements = this._evaluator._queryCSS({ scope: root, pierceShadow: shadow }, \"*\");\n for (const element of elements)\n appendElement(element);\n return result;\n };\n return { queryAll };\n }\n _createInternalHasTextEngine() {\n return {\n queryAll: (root, selector) => {\n if (root.nodeType !== 1)\n return [];\n const element = root;\n const text = elementText(this._evaluator._cacheText, element);\n const { matcher } = createTextMatcher(selector, true);\n return matcher(text) ? [element] : [];\n }\n };\n }\n _createInternalHasNotTextEngine() {\n return {\n queryAll: (root, selector) => {\n if (root.nodeType !== 1)\n return [];\n const element = root;\n const text = elementText(this._evaluator._cacheText, element);\n const { matcher } = createTextMatcher(selector, true);\n return matcher(text) ? [] : [element];\n }\n };\n }\n _createInternalLabelEngine() {\n return {\n queryAll: (root, selector) => {\n const { matcher } = createTextMatcher(selector, true);\n const allElements = this._evaluator._queryCSS({ scope: root, pierceShadow: true }, \"*\");\n return allElements.filter((element) => {\n return getElementLabels(this._evaluator._cacheText, element).some((label) => matcher(label));\n });\n }\n };\n }\n _createNamedAttributeEngine() {\n const queryAll = (root, selector) => {\n const parsed = parseAttributeSelector(selector, true);\n if (parsed.name || parsed.attributes.length !== 1)\n throw new Error(\"Malformed attribute selector: \" + selector);\n const { name, value, caseSensitive } = parsed.attributes[0];\n const lowerCaseValue = caseSensitive ? null : value.toLowerCase();\n let matcher;\n if (value instanceof RegExp)\n matcher = (s) => !!s.match(value);\n else if (caseSensitive)\n matcher = (s) => s === value;\n else\n matcher = (s) => s.toLowerCase().includes(lowerCaseValue);\n const elements = this._evaluator._queryCSS({ scope: root, pierceShadow: true }, `[${name}]`);\n return elements.filter((e) => matcher(e.getAttribute(name)));\n };\n return { queryAll };\n }\n _createControlEngine() {\n return {\n queryAll(root, body) {\n if (body === \"enter-frame\")\n return [];\n if (body === \"return-empty\")\n return [];\n if (body === \"component\") {\n if (root.nodeType !== 1)\n return [];\n return [root.childElementCount === 1 ? root.firstElementChild : root];\n }\n throw new Error(`Internal error, unknown internal:control selector ${body}`);\n }\n };\n }\n _createHasEngine() {\n const queryAll = (root, body) => {\n if (root.nodeType !== 1)\n return [];\n const has = !!this.querySelector(body.parsed, root, false);\n return has ? [root] : [];\n };\n return { queryAll };\n }\n _createHasNotEngine() {\n const queryAll = (root, body) => {\n if (root.nodeType !== 1)\n return [];\n const has = !!this.querySelector(body.parsed, root, false);\n return has ? [] : [root];\n };\n return { queryAll };\n }\n _createVisibleEngine() {\n const queryAll = (root, body) => {\n if (root.nodeType !== 1)\n return [];\n return isElementVisible(root) === Boolean(body) ? [root] : [];\n };\n return { queryAll };\n }\n extend(source, params) {\n const constrFunction = this.window.eval(`\n (() => {\n const module = {};\n ${source}\n return module.exports.default();\n })()`);\n return new constrFunction(this, params);\n }\n isVisible(element) {\n return isElementVisible(element);\n }\n async viewportRatio(element) {\n return await new Promise((resolve) => {\n const observer = new IntersectionObserver((entries) => {\n resolve(entries[0].intersectionRatio);\n observer.disconnect();\n });\n observer.observe(element);\n requestAnimationFrame(() => {\n });\n });\n }\n pollRaf(predicate) {\n return this.poll(predicate, (next) => requestAnimationFrame(next));\n }\n poll(predicate, scheduleNext) {\n return this._runAbortableTask((progress) => {\n let fulfill;\n let reject;\n const result = new Promise((f, r) => {\n fulfill = f;\n reject = r;\n });\n const next = () => {\n if (progress.aborted)\n return;\n try {\n const success = predicate(progress);\n if (success !== progress.continuePolling)\n fulfill(success);\n else\n scheduleNext(next);\n } catch (e) {\n progress.log(\" \" + e.message);\n reject(e);\n }\n };\n next();\n return result;\n });\n }\n _runAbortableTask(task) {\n let unsentLog = [];\n let takeNextLogsCallback;\n let taskFinished = false;\n const logReady = () => {\n if (!takeNextLogsCallback)\n return;\n takeNextLogsCallback(unsentLog);\n unsentLog = [];\n takeNextLogsCallback = void 0;\n };\n const takeNextLogs = () => new Promise((fulfill) => {\n takeNextLogsCallback = fulfill;\n if (unsentLog.length || taskFinished)\n logReady();\n });\n let lastMessage = \"\";\n const progress = {\n injectedScript: this,\n aborted: false,\n continuePolling: Symbol(\"continuePolling\"),\n log: (message) => {\n lastMessage = message;\n unsentLog.push({ message });\n logReady();\n },\n logRepeating: (message) => {\n if (message !== lastMessage)\n progress.log(message);\n }\n };\n const run = () => {\n const result = task(progress);\n result.finally(() => {\n taskFinished = true;\n logReady();\n });\n return result;\n };\n return {\n takeNextLogs,\n run,\n cancel: () => {\n progress.aborted = true;\n },\n takeLastLogs: () => unsentLog\n };\n }\n getElementBorderWidth(node) {\n if (node.nodeType !== Node.ELEMENT_NODE || !node.ownerDocument || !node.ownerDocument.defaultView)\n return { left: 0, top: 0 };\n const style = node.ownerDocument.defaultView.getComputedStyle(node);\n return { left: parseInt(style.borderLeftWidth || \"\", 10), top: parseInt(style.borderTopWidth || \"\", 10) };\n }\n describeIFrameStyle(iframe) {\n if (!iframe.ownerDocument || !iframe.ownerDocument.defaultView)\n return \"error:notconnected\";\n const defaultView = iframe.ownerDocument.defaultView;\n for (let e = iframe; e; e = parentElementOrShadowHost(e)) {\n if (defaultView.getComputedStyle(e).transform !== \"none\")\n return \"transformed\";\n }\n const iframeStyle = defaultView.getComputedStyle(iframe);\n return {\n left: parseInt(iframeStyle.borderLeftWidth || \"\", 10) + parseInt(iframeStyle.paddingLeft || \"\", 10),\n top: parseInt(iframeStyle.borderTopWidth || \"\", 10) + parseInt(iframeStyle.paddingTop || \"\", 10)\n };\n }\n retarget(node, behavior) {\n let element = node.nodeType === Node.ELEMENT_NODE ? node : node.parentElement;\n if (!element)\n return null;\n if (behavior === \"none\")\n return element;\n if (!element.matches(\"input, textarea, select\")) {\n if (behavior === \"button-link\")\n element = element.closest(\"button, [role=button], a, [role=link]\") || element;\n else\n element = element.closest(\"button, [role=button], [role=checkbox], [role=radio]\") || element;\n }\n if (behavior === \"follow-label\") {\n if (!element.matches(\"input, textarea, button, select, [role=button], [role=checkbox], [role=radio]\") && !element.isContentEditable) {\n element = element.closest(\"label\") || element;\n }\n if (element.nodeName === \"LABEL\")\n element = element.control || element;\n }\n return element;\n }\n waitForElementStatesAndPerformAction(node, states, force, callback) {\n let lastRect;\n let counter = 0;\n let samePositionCounter = 0;\n let lastTime = 0;\n return this.pollRaf((progress) => {\n if (force) {\n progress.log(` forcing action`);\n return callback(node, progress);\n }\n for (const state of states) {\n if (state !== \"stable\") {\n const result = this.elementState(node, state);\n if (typeof result !== \"boolean\")\n return result;\n if (!result) {\n progress.logRepeating(` element is not ${state} - waiting...`);\n return progress.continuePolling;\n }\n continue;\n }\n const element = this.retarget(node, \"no-follow-label\");\n if (!element)\n return \"error:notconnected\";\n if (++counter === 1)\n return progress.continuePolling;\n const time = performance.now();\n if (this._stableRafCount > 1 && time - lastTime < 15)\n return progress.continuePolling;\n lastTime = time;\n const clientRect = element.getBoundingClientRect();\n const rect = { x: clientRect.top, y: clientRect.left, width: clientRect.width, height: clientRect.height };\n const samePosition = lastRect && rect.x === lastRect.x && rect.y === lastRect.y && rect.width === lastRect.width && rect.height === lastRect.height;\n if (samePosition)\n ++samePositionCounter;\n else\n samePositionCounter = 0;\n const isStable = samePositionCounter >= this._stableRafCount;\n const isStableForLogs = isStable || !lastRect;\n lastRect = rect;\n if (!isStableForLogs)\n progress.logRepeating(` element is not stable - waiting...`);\n if (!isStable)\n return progress.continuePolling;\n }\n return callback(node, progress);\n });\n }\n elementState(node, state) {\n const element = this.retarget(node, [\"stable\", \"visible\", \"hidden\"].includes(state) ? \"none\" : \"follow-label\");\n if (!element || !element.isConnected) {\n if (state === \"hidden\")\n return true;\n return \"error:notconnected\";\n }\n if (state === \"visible\")\n return this.isVisible(element);\n if (state === \"hidden\")\n return !this.isVisible(element);\n const disabled = getAriaDisabled(element);\n if (state === \"disabled\")\n return disabled;\n if (state === \"enabled\")\n return !disabled;\n const editable = !([\"INPUT\", \"TEXTAREA\", \"SELECT\"].includes(element.nodeName) && element.hasAttribute(\"readonly\"));\n if (state === \"editable\")\n return !disabled && editable;\n if (state === \"checked\" || state === \"unchecked\") {\n const need = state === \"checked\";\n const checked = getChecked(element, false);\n if (checked === \"error\")\n throw this.createStacklessError(\"Not a checkbox or radio button\");\n return need === checked;\n }\n throw this.createStacklessError(`Unexpected element state \"${state}\"`);\n }\n selectOptions(optionsToSelect, node, progress) {\n const element = this.retarget(node, \"follow-label\");\n if (!element)\n return \"error:notconnected\";\n if (element.nodeName.toLowerCase() !== \"select\")\n throw this.createStacklessError(\"Element is not a <select> element\");\n const select = element;\n const options = [...select.options];\n const selectedOptions = [];\n let remainingOptionsToSelect = optionsToSelect.slice();\n for (let index = 0; index < options.length; index++) {\n const option = options[index];\n const filter = (optionToSelect) => {\n if (optionToSelect instanceof Node)\n return option === optionToSelect;\n let matches = true;\n if (optionToSelect.valueOrLabel !== void 0)\n matches = matches && (optionToSelect.valueOrLabel === option.value || optionToSelect.valueOrLabel === option.label);\n if (optionToSelect.value !== void 0)\n matches = matches && optionToSelect.value === option.value;\n if (optionToSelect.label !== void 0)\n matches = matches && optionToSelect.label === option.label;\n if (optionToSelect.index !== void 0)\n matches = matches && optionToSelect.index === index;\n return matches;\n };\n if (!remainingOptionsToSelect.some(filter))\n continue;\n selectedOptions.push(option);\n if (select.multiple) {\n remainingOptionsToSelect = remainingOptionsToSelect.filter((o) => !filter(o));\n } else {\n remainingOptionsToSelect = [];\n break;\n }\n }\n if (remainingOptionsToSelect.length) {\n progress.logRepeating(\" did not find some options - waiting... \");\n return progress.continuePolling;\n }\n select.value = void 0;\n selectedOptions.forEach((option) => option.selected = true);\n progress.log(\" selected specified option(s)\");\n select.dispatchEvent(new Event(\"input\", { \"bubbles\": true }));\n select.dispatchEvent(new Event(\"change\", { \"bubbles\": true }));\n return selectedOptions.map((option) => option.value);\n }\n fill(value, node, progress) {\n const element = this.retarget(node, \"follow-label\");\n if (!element)\n return \"error:notconnected\";\n if (element.nodeName.toLowerCase() === \"input\") {\n const input = element;\n const type = input.type.toLowerCase();\n const kInputTypesToSetValue = /* @__PURE__ */ new Set([\"color\", \"date\", \"time\", \"datetime\", \"datetime-local\", \"month\", \"range\", \"week\"]);\n const kInputTypesToTypeInto = /* @__PURE__ */ new Set([\"\", \"email\", \"number\", \"password\", \"search\", \"tel\", \"text\", \"url\"]);\n if (!kInputTypesToTypeInto.has(type) && !kInputTypesToSetValue.has(type)) {\n progress.log(` input of type \"${type}\" cannot be filled`);\n throw this.createStacklessError(`Input of type \"${type}\" cannot be filled`);\n }\n if (type === \"number\") {\n value = value.trim();\n if (isNaN(Number(value)))\n throw this.createStacklessError(\"Cannot type text into input[type=number]\");\n }\n if (kInputTypesToSetValue.has(type)) {\n value = value.trim();\n input.focus();\n input.value = value;\n if (input.value !== value)\n throw this.createStacklessError(\"Malformed value\");\n element.dispatchEvent(new Event(\"input\", { \"bubbles\": true }));\n element.dispatchEvent(new Event(\"change\", { \"bubbles\": true }));\n return \"done\";\n }\n } else if (element.nodeName.toLowerCase() === \"textarea\") {\n } else if (!element.isContentEditable) {\n throw this.createStacklessError(\"Element is not an <input>, <textarea> or [contenteditable] element\");\n }\n this.selectText(element);\n return \"needsinput\";\n }\n selectText(node) {\n const element = this.retarget(node, \"follow-label\");\n if (!element)\n return \"error:notconnected\";\n if (element.nodeName.toLowerCase() === \"input\") {\n const input = element;\n input.select();\n input.focus();\n return \"done\";\n }\n if (element.nodeName.toLowerCase() === \"textarea\") {\n const textarea = element;\n textarea.selectionStart = 0;\n textarea.selectionEnd = textarea.value.length;\n textarea.focus();\n return \"done\";\n }\n const range = element.ownerDocument.createRange();\n range.selectNodeContents(element);\n const selection = element.ownerDocument.defaultView.getSelection();\n if (selection) {\n selection.removeAllRanges();\n selection.addRange(range);\n }\n element.focus();\n return \"done\";\n }\n _activelyFocused(node) {\n const activeElement = node.getRootNode().activeElement;\n const isFocused = activeElement === node && !!node.ownerDocument && node.ownerDocument.hasFocus();\n return { activeElement, isFocused };\n }\n focusNode(node, resetSelectionIfNotFocused) {\n if (!node.isConnected)\n return \"error:notconnected\";\n if (node.nodeType !== Node.ELEMENT_NODE)\n throw this.createStacklessError(\"Node is not an element\");\n const { activeElement, isFocused: wasFocused } = this._activelyFocused(node);\n if (node.isContentEditable && !wasFocused && activeElement && activeElement.blur) {\n activeElement.blur();\n }\n node.focus();\n node.focus();\n if (resetSelectionIfNotFocused && !wasFocused && node.nodeName.toLowerCase() === \"input\") {\n try {\n const input = node;\n input.setSelectionRange(0, 0);\n } catch (e) {\n }\n }\n return \"done\";\n }\n blurNode(node) {\n if (!node.isConnected)\n return \"error:notconnected\";\n if (node.nodeType !== Node.ELEMENT_NODE)\n throw this.createStacklessError(\"Node is not an element\");\n node.blur();\n return \"done\";\n }\n setInputFiles(node, payloads) {\n if (node.nodeType !== Node.ELEMENT_NODE)\n return \"Node is not of type HTMLElement\";\n const element = node;\n if (element.nodeName !== \"INPUT\")\n return \"Not an <input> element\";\n const input = element;\n const type = (input.getAttribute(\"type\") || \"\").toLowerCase();\n if (type !== \"file\")\n return \"Not an input[type=file] element\";\n const files = payloads.map((file) => {\n const bytes = Uint8Array.from(atob(file.buffer), (c) => c.charCodeAt(0));\n return new File([bytes], file.name, { type: file.mimeType });\n });\n const dt = new DataTransfer();\n for (const file of files)\n dt.items.add(file);\n input.files = dt.files;\n input.dispatchEvent(new Event(\"input\", { \"bubbles\": true }));\n input.dispatchEvent(new Event(\"change\", { \"bubbles\": true }));\n }\n expectHitTarget(hitPoint, targetElement) {\n const roots = [];\n let parentElement = targetElement;\n while (parentElement) {\n const root = enclosingShadowRootOrDocument(parentElement);\n if (!root)\n break;\n roots.push(root);\n if (root.nodeType === 9)\n break;\n parentElement = root.host;\n }\n let hitElement;\n for (let index = roots.length - 1; index >= 0; index--) {\n const root = roots[index];\n const elements = root.elementsFromPoint(hitPoint.x, hitPoint.y);\n const singleElement = root.elementFromPoint(hitPoint.x, hitPoint.y);\n if (singleElement && elements[0] && parentElementOrShadowHost(singleElement) === elements[0]) {\n const style = this.window.getComputedStyle(singleElement);\n if ((style == null ? void 0 : style.display) === \"contents\") {\n elements.unshift(singleElement);\n }\n }\n if (elements[0] && elements[0].shadowRoot === root && elements[1] === singleElement) {\n elements.shift();\n }\n const innerElement = elements[0];\n if (!innerElement)\n break;\n hitElement = innerElement;\n if (index && innerElement !== roots[index - 1].host)\n break;\n }\n const hitParents = [];\n while (hitElement && hitElement !== targetElement) {\n hitParents.push(hitElement);\n hitElement = parentElementOrShadowHost(hitElement);\n }\n if (hitElement === targetElement)\n return \"done\";\n const hitTargetDescription = this.previewNode(hitParents[0] || this.document.documentElement);\n let rootHitTargetDescription;\n let element = targetElement;\n while (element) {\n const index = hitParents.indexOf(element);\n if (index !== -1) {\n if (index > 1)\n rootHitTargetDescription = this.previewNode(hitParents[index - 1]);\n break;\n }\n element = parentElementOrShadowHost(element);\n }\n if (rootHitTargetDescription)\n return { hitTargetDescription: `${hitTargetDescription} from ${rootHitTargetDescription} subtree` };\n return { hitTargetDescription };\n }\n setupHitTargetInterceptor(node, action, hitPoint, blockAllEvents) {\n const element = this.retarget(node, \"button-link\");\n if (!element || !element.isConnected)\n return \"error:notconnected\";\n if (hitPoint) {\n const preliminaryResult = this.expectHitTarget(hitPoint, element);\n if (preliminaryResult !== \"done\")\n return preliminaryResult.hitTargetDescription;\n }\n if (action === \"drag\")\n return { stop: () => \"done\" };\n const events = {\n \"hover\": kHoverHitTargetInterceptorEvents,\n \"tap\": kTapHitTargetInterceptorEvents,\n \"mouse\": kMouseHitTargetInterceptorEvents\n }[action];\n let result;\n const listener = (event) => {\n if (!events.has(event.type))\n return;\n if (!event.isTrusted)\n return;\n const point = !!this.window.TouchEvent && event instanceof this.window.TouchEvent ? event.touches[0] : event;\n if (result === void 0 && point)\n result = this.expectHitTarget({ x: point.clientX, y: point.clientY }, element);\n if (blockAllEvents || result !== \"done\" && result !== void 0) {\n event.preventDefault();\n event.stopPropagation();\n event.stopImmediatePropagation();\n }\n };\n const stop = () => {\n if (this._hitTargetInterceptor === listener)\n this._hitTargetInterceptor = void 0;\n return result || \"done\";\n };\n this._hitTargetInterceptor = listener;\n return { stop };\n }\n dispatchEvent(node, type, eventInit) {\n let event;\n eventInit = { bubbles: true, cancelable: true, composed: true, ...eventInit };\n switch (eventType.get(type)) {\n case \"mouse\":\n event = new MouseEvent(type, eventInit);\n break;\n case \"keyboard\":\n event = new KeyboardEvent(type, eventInit);\n break;\n case \"touch\":\n event = new TouchEvent(type, eventInit);\n break;\n case \"pointer\":\n event = new PointerEvent(type, eventInit);\n break;\n case \"focus\":\n event = new FocusEvent(type, eventInit);\n break;\n case \"drag\":\n event = new DragEvent(type, eventInit);\n break;\n case \"wheel\":\n event = new WheelEvent(type, eventInit);\n break;\n default:\n event = new Event(type, eventInit);\n break;\n }\n node.dispatchEvent(event);\n }\n previewNode(node) {\n if (node.nodeType === Node.TEXT_NODE)\n return oneLine(`#text=${node.nodeValue || \"\"}`);\n if (node.nodeType !== Node.ELEMENT_NODE)\n return oneLine(`<${node.nodeName.toLowerCase()} />`);\n const element = node;\n const attrs = [];\n for (let i = 0; i < element.attributes.length; i++) {\n const { name, value } = element.attributes[i];\n if (name === \"style\")\n continue;\n if (!value && booleanAttributes.has(name))\n attrs.push(` ${name}`);\n else\n attrs.push(` ${name}=\"${value}\"`);\n }\n attrs.sort((a, b) => a.length - b.length);\n let attrText = attrs.join(\"\");\n if (attrText.length > 50)\n attrText = attrText.substring(0, 49) + \"\\u2026\";\n if (autoClosingTags.has(element.nodeName))\n return oneLine(`<${element.nodeName.toLowerCase()}${attrText}/>`);\n const children = element.childNodes;\n let onlyText = false;\n if (children.length <= 5) {\n onlyText = true;\n for (let i = 0; i < children.length; i++)\n onlyText = onlyText && children[i].nodeType === Node.TEXT_NODE;\n }\n let text = onlyText ? element.textContent || \"\" : children.length ? \"\\u2026\" : \"\";\n if (text.length > 50)\n text = text.substring(0, 49) + \"\\u2026\";\n return oneLine(`<${element.nodeName.toLowerCase()}${attrText}>${text}</${element.nodeName.toLowerCase()}>`);\n }\n strictModeViolationError(selector, matches) {\n const infos = matches.slice(0, 10).map((m) => ({\n preview: this.previewNode(m),\n selector: this.generateSelector(m)\n }));\n const lines = infos.map((info, i) => `\n ${i + 1}) ${info.preview} aka ${asLocator(this._sdkLanguage, info.selector)}`);\n if (infos.length < matches.length)\n lines.push(\"\\n ...\");\n return this.createStacklessError(`strict mode violation: ${asLocator(this._sdkLanguage, stringifySelector(selector))} resolved to ${matches.length} elements:${lines.join(\"\")}\n`);\n }\n createStacklessError(message) {\n if (this._browserName === \"firefox\") {\n const error2 = new Error(\"Error: \" + message);\n error2.stack = \"\";\n return error2;\n }\n const error = new Error(message);\n delete error.stack;\n return error;\n }\n maskSelectors(selectors, color) {\n if (this._highlight)\n this.hideHighlight();\n this._highlight = new Highlight(this);\n this._highlight.install();\n const elements = [];\n for (const selector of selectors)\n elements.push(this.querySelectorAll(selector, this.document.documentElement));\n this._highlight.maskElements(elements.flat(), color);\n }\n highlight(selector) {\n if (!this._highlight) {\n this._highlight = new Highlight(this);\n this._highlight.install();\n }\n this._highlight.runHighlightOnRaf(selector);\n }\n hideHighlight() {\n if (this._highlight) {\n this._highlight.uninstall();\n delete this._highlight;\n }\n }\n markTargetElements(markedElements, callId) {\n const customEvent = new CustomEvent(\"__playwright_target__\", {\n bubbles: true,\n cancelable: true,\n detail: callId,\n composed: false\n });\n for (const element of markedElements)\n element.dispatchEvent(customEvent);\n }\n _setupGlobalListenersRemovalDetection() {\n const customEventName = \"__playwright_global_listeners_check__\";\n let seenEvent = false;\n const handleCustomEvent = () => seenEvent = true;\n this.window.addEventListener(customEventName, handleCustomEvent);\n new MutationObserver((entries) => {\n const newDocumentElement = entries.some((entry) => Array.from(entry.addedNodes).includes(this.document.documentElement));\n if (!newDocumentElement)\n return;\n seenEvent = false;\n this.window.dispatchEvent(new CustomEvent(customEventName));\n if (seenEvent)\n return;\n this.window.addEventListener(customEventName, handleCustomEvent);\n for (const callback of this.onGlobalListenersRemoved)\n callback();\n }).observe(this.document, { childList: true });\n }\n _setupHitTargetInterceptors() {\n const listener = (event) => {\n var _a;\n return (_a = this._hitTargetInterceptor) == null ? void 0 : _a.call(this, event);\n };\n const addHitTargetInterceptorListeners = () => {\n for (const event of kAllHitTargetInterceptorEvents)\n this.window.addEventListener(event, listener, { capture: true, passive: false });\n };\n addHitTargetInterceptorListeners();\n this.onGlobalListenersRemoved.add(addHitTargetInterceptorListeners);\n }\n async expect(element, options, elements) {\n const isArray = options.expression === \"to.have.count\" || options.expression.endsWith(\".array\");\n if (isArray)\n return this.expectArray(elements, options);\n if (!element) {\n if (!options.isNot && options.expression === \"to.be.hidden\")\n return { matches: true };\n if (options.isNot && options.expression === \"to.be.visible\")\n return { matches: false };\n if (!options.isNot && options.expression === \"to.be.detached\")\n return { matches: true };\n if (options.isNot && options.expression === \"to.be.attached\")\n return { matches: false };\n if (options.isNot && options.expression === \"to.be.in.viewport\")\n return { matches: false };\n return { matches: options.isNot, missingRecevied: true };\n }\n return await this.expectSingleElement(element, options);\n }\n async expectSingleElement(element, options) {\n var _a, _b;\n const expression = options.expression;\n {\n let elementState;\n if (expression === \"to.be.checked\") {\n elementState = this.elementState(element, \"checked\");\n } else if (expression === \"to.be.unchecked\") {\n elementState = this.elementState(element, \"unchecked\");\n } else if (expression === \"to.be.disabled\") {\n elementState = this.elementState(element, \"disabled\");\n } else if (expression === \"to.be.editable\") {\n elementState = this.elementState(element, \"editable\");\n } else if (expression === \"to.be.readonly\") {\n elementState = !this.elementState(element, \"editable\");\n } else if (expression === \"to.be.empty\") {\n if (element.nodeName === \"INPUT\" || element.nodeName === \"TEXTAREA\")\n elementState = !element.value;\n else\n elementState = !((_a = element.textContent) == null ? void 0 : _a.trim());\n } else if (expression === \"to.be.enabled\") {\n elementState = this.elementState(element, \"enabled\");\n } else if (expression === \"to.be.focused\") {\n elementState = this._activelyFocused(element).isFocused;\n } else if (expression === \"to.be.hidden\") {\n elementState = this.elementState(element, \"hidden\");\n } else if (expression === \"to.be.visible\") {\n elementState = this.elementState(element, \"visible\");\n } else if (expression === \"to.be.attached\") {\n elementState = true;\n } else if (expression === \"to.be.detached\") {\n elementState = false;\n }\n if (elementState !== void 0) {\n if (elementState === \"error:notcheckbox\")\n throw this.createStacklessError(\"Element is not a checkbox\");\n if (elementState === \"error:notconnected\")\n throw this.createStacklessError(\"Element is not connected\");\n return { received: elementState, matches: elementState };\n }\n }\n {\n if (expression === \"to.have.property\") {\n const received = element[options.expressionArg];\n const matches = deepEquals(received, options.expectedValue);\n return { received, matches };\n }\n }\n {\n if (expression === \"to.be.in.viewport\") {\n const ratio = await this.viewportRatio(element);\n return { received: `viewport ratio ${ratio}`, matches: ratio > 0 && ratio > ((_b = options.expectedNumber) != null ? _b : 0) - 1e-9 };\n }\n }\n {\n if (expression === \"to.have.values\") {\n element = this.retarget(element, \"follow-label\");\n if (element.nodeName !== \"SELECT\" || !element.multiple)\n throw this.createStacklessError(\"Not a select element with a multiple attribute\");\n const received = [...element.selectedOptions].map((o) => o.value);\n if (received.length !== options.expectedText.length)\n return { received, matches: false };\n return { received, matches: received.map((r, i) => new ExpectedTextMatcher(options.expectedText[i]).matches(r)).every(Boolean) };\n }\n }\n {\n let received;\n if (expression === \"to.have.attribute\") {\n const value = element.getAttribute(options.expressionArg);\n if (value === null)\n return { received: null, matches: false };\n received = value;\n } else if (expression === \"to.have.class\") {\n received = element.classList.toString();\n } else if (expression === \"to.have.css\") {\n received = this.window.getComputedStyle(element).getPropertyValue(options.expressionArg);\n } else if (expression === \"to.have.id\") {\n received = element.id;\n } else if (expression === \"to.have.text\") {\n received = options.useInnerText ? element.innerText : elementText(/* @__PURE__ */ new Map(), element).full;\n } else if (expression === \"to.have.title\") {\n received = this.document.title;\n } else if (expression === \"to.have.url\") {\n received = this.document.location.href;\n } else if (expression === \"to.have.value\") {\n element = this.retarget(element, \"follow-label\");\n if (element.nodeName !== \"INPUT\" && element.nodeName !== \"TEXTAREA\" && element.nodeName !== \"SELECT\")\n throw this.createStacklessError(\"Not an input element\");\n received = element.value;\n }\n if (received !== void 0 && options.expectedText) {\n const matcher = new ExpectedTextMatcher(options.expectedText[0]);\n return { received, matches: matcher.matches(received) };\n }\n }\n throw this.createStacklessError(\"Unknown expect matcher: \" + expression);\n }\n expectArray(elements, options) {\n const expression = options.expression;\n if (expression === \"to.have.count\") {\n const received2 = elements.length;\n const matches = received2 === options.expectedNumber;\n return { received: received2, matches };\n }\n let received;\n if (expression === \"to.have.text.array\" || expression === \"to.contain.text.array\")\n received = elements.map((e) => options.useInnerText ? e.innerText : elementText(/* @__PURE__ */ new Map(), e).full);\n else if (expression === \"to.have.class.array\")\n received = elements.map((e) => e.classList.toString());\n if (received && options.expectedText) {\n const lengthShouldMatch = expression !== \"to.contain.text.array\";\n const matchesLength = received.length === options.expectedText.length || !lengthShouldMatch;\n if (!matchesLength)\n return { received, matches: false };\n const matchers = options.expectedText.map((e) => new ExpectedTextMatcher(e));\n let mIndex = 0, rIndex = 0;\n while (mIndex < matchers.length && rIndex < received.length) {\n if (matchers[mIndex].matches(received[rIndex]))\n ++mIndex;\n ++rIndex;\n }\n return { received, matches: mIndex === matchers.length };\n }\n throw this.createStacklessError(\"Unknown expect matcher: \" + expression);\n }\n getElementAccessibleName(element, includeHidden) {\n return getElementAccessibleName(element, !!includeHidden);\n }\n getAriaRole(element) {\n return getAriaRole(element);\n }\n};\nvar autoClosingTags = /* @__PURE__ */ new Set([\"AREA\", \"BASE\", \"BR\", \"COL\", \"COMMAND\", \"EMBED\", \"HR\", \"IMG\", \"INPUT\", \"KEYGEN\", \"LINK\", \"MENUITEM\", \"META\", \"PARAM\", \"SOURCE\", \"TRACK\", \"WBR\"]);\nvar booleanAttributes = /* @__PURE__ */ new Set([\"checked\", \"selected\", \"disabled\", \"readonly\", \"multiple\"]);\nfunction oneLine(s) {\n return s.replace(/\\n/g, \"\\u21B5\").replace(/\\t/g, \"\\u21C6\");\n}\nvar eventType = /* @__PURE__ */ new Map([\n [\"auxclick\", \"mouse\"],\n [\"click\", \"mouse\"],\n [\"dblclick\", \"mouse\"],\n [\"mousedown\", \"mouse\"],\n [\"mouseeenter\", \"mouse\"],\n [\"mouseleave\", \"mouse\"],\n [\"mousemove\", \"mouse\"],\n [\"mouseout\", \"mouse\"],\n [\"mouseover\", \"mouse\"],\n [\"mouseup\", \"mouse\"],\n [\"mouseleave\", \"mouse\"],\n [\"mousewheel\", \"mouse\"],\n [\"keydown\", \"keyboard\"],\n [\"keyup\", \"keyboard\"],\n [\"keypress\", \"keyboard\"],\n [\"textInput\", \"keyboard\"],\n [\"touchstart\", \"touch\"],\n [\"touchmove\", \"touch\"],\n [\"touchend\", \"touch\"],\n [\"touchcancel\", \"touch\"],\n [\"pointerover\", \"pointer\"],\n [\"pointerout\", \"pointer\"],\n [\"pointerenter\", \"pointer\"],\n [\"pointerleave\", \"pointer\"],\n [\"pointerdown\", \"pointer\"],\n [\"pointerup\", \"pointer\"],\n [\"pointermove\", \"pointer\"],\n [\"pointercancel\", \"pointer\"],\n [\"gotpointercapture\", \"pointer\"],\n [\"lostpointercapture\", \"pointer\"],\n [\"focus\", \"focus\"],\n [\"blur\", \"focus\"],\n [\"drag\", \"drag\"],\n [\"dragstart\", \"drag\"],\n [\"dragend\", \"drag\"],\n [\"dragover\", \"drag\"],\n [\"dragenter\", \"drag\"],\n [\"dragleave\", \"drag\"],\n [\"dragexit\", \"drag\"],\n [\"drop\", \"drag\"],\n [\"wheel\", \"wheel\"]\n]);\nvar kHoverHitTargetInterceptorEvents = /* @__PURE__ */ new Set([\"mousemove\"]);\nvar kTapHitTargetInterceptorEvents = /* @__PURE__ */ new Set([\"pointerdown\", \"pointerup\", \"touchstart\", \"touchend\", \"touchcancel\"]);\nvar kMouseHitTargetInterceptorEvents = /* @__PURE__ */ new Set([\"mousedown\", \"mouseup\", \"pointerdown\", \"pointerup\", \"click\", \"auxclick\", \"dblclick\", \"contextmenu\"]);\nvar kAllHitTargetInterceptorEvents = /* @__PURE__ */ new Set([...kHoverHitTargetInterceptorEvents, ...kTapHitTargetInterceptorEvents, ...kMouseHitTargetInterceptorEvents]);\nfunction cssUnquote(s) {\n s = s.substring(1, s.length - 1);\n if (!s.includes(\"\\\\\"))\n return s;\n const r = [];\n let i = 0;\n while (i < s.length) {\n if (s[i] === \"\\\\\" && i + 1 < s.length)\n i++;\n r.push(s[i++]);\n }\n return r.join(\"\");\n}\nfunction createTextMatcher(selector, internal) {\n if (selector[0] === \"/\" && selector.lastIndexOf(\"/\") > 0) {\n const lastSlash = selector.lastIndexOf(\"/\");\n const re = new RegExp(selector.substring(1, lastSlash), selector.substring(lastSlash + 1));\n return { matcher: (elementText2) => re.test(elementText2.full), kind: \"regex\" };\n }\n const unquote = internal ? JSON.parse.bind(JSON) : cssUnquote;\n let strict = false;\n if (selector.length > 1 && selector[0] === '\"' && selector[selector.length - 1] === '\"') {\n selector = unquote(selector);\n strict = true;\n } else if (internal && selector.length > 1 && selector[0] === '\"' && selector[selector.length - 2] === '\"' && selector[selector.length - 1] === \"i\") {\n selector = unquote(selector.substring(0, selector.length - 1));\n strict = false;\n } else if (internal && selector.length > 1 && selector[0] === '\"' && selector[selector.length - 2] === '\"' && selector[selector.length - 1] === \"s\") {\n selector = unquote(selector.substring(0, selector.length - 1));\n strict = true;\n } else if (selector.length > 1 && selector[0] === \"'\" && selector[selector.length - 1] === \"'\") {\n selector = unquote(selector);\n strict = true;\n }\n selector = normalizeWhiteSpace(selector);\n if (strict) {\n if (internal)\n return { kind: \"strict\", matcher: (elementText2) => normalizeWhiteSpace(elementText2.full) === selector };\n const strictTextNodeMatcher = (elementText2) => {\n if (!selector && !elementText2.immediate.length)\n return true;\n return elementText2.immediate.some((s) => normalizeWhiteSpace(s) === selector);\n };\n return { matcher: strictTextNodeMatcher, kind: \"strict\" };\n }\n selector = selector.toLowerCase();\n return { kind: \"lax\", matcher: (elementText2) => normalizeWhiteSpace(elementText2.full).toLowerCase().includes(selector) };\n}\nvar ExpectedTextMatcher = class {\n constructor(expected) {\n this._normalizeWhiteSpace = expected.normalizeWhiteSpace;\n this._ignoreCase = expected.ignoreCase;\n this._string = expected.matchSubstring ? void 0 : this.normalize(expected.string);\n this._substring = expected.matchSubstring ? this.normalize(expected.string) : void 0;\n if (expected.regexSource) {\n const flags = new Set((expected.regexFlags || \"\").split(\"\"));\n if (expected.ignoreCase === false)\n flags.delete(\"i\");\n if (expected.ignoreCase === true)\n flags.add(\"i\");\n this._regex = new RegExp(expected.regexSource, [...flags].join(\"\"));\n }\n }\n matches(text) {\n if (!this._regex)\n text = this.normalize(text);\n if (this._string !== void 0)\n return text === this._string;\n if (this._substring !== void 0)\n return text.includes(this._substring);\n if (this._regex)\n return !!this._regex.test(text);\n return false;\n }\n normalize(s) {\n if (!s)\n return s;\n if (this._normalizeWhiteSpace)\n s = normalizeWhiteSpace(s);\n if (this._ignoreCase)\n s = s.toLocaleLowerCase();\n return s;\n }\n};\nfunction deepEquals(a, b) {\n if (a === b)\n return true;\n if (a && b && typeof a === \"object\" && typeof b === \"object\") {\n if (a.constructor !== b.constructor)\n return false;\n if (Array.isArray(a)) {\n if (a.length !== b.length)\n return false;\n for (let i = 0; i < a.length; ++i) {\n if (!deepEquals(a[i], b[i]))\n return false;\n }\n return true;\n }\n if (a instanceof RegExp)\n return a.source === b.source && a.flags === b.flags;\n if (a.valueOf !== Object.prototype.valueOf)\n return a.valueOf() === b.valueOf();\n if (a.toString !== Object.prototype.toString)\n return a.toString() === b.toString();\n const keys = Object.keys(a);\n if (keys.length !== Object.keys(b).length)\n return false;\n for (let i = 0; i < keys.length; ++i) {\n if (!b.hasOwnProperty(keys[i]))\n return false;\n }\n for (const key of keys) {\n if (!deepEquals(a[key], b[key]))\n return false;\n }\n return true;\n }\n if (typeof a === \"number\" && typeof b === \"number\")\n return isNaN(a) && isNaN(b);\n return false;\n}\n";
  7. exports.source = source;