UNPKG

ivya

Version:

Fork of Playwright's locator resolution

1,681 lines (1,672 loc) 162 kB
//#region \0@oxc-project+runtime@0.115.0/helpers/typeof.js function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function(o) { return typeof o; } : function(o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); } //#endregion //#region \0@oxc-project+runtime@0.115.0/helpers/toPrimitive.js function toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } //#endregion //#region \0@oxc-project+runtime@0.115.0/helpers/toPropertyKey.js function toPropertyKey(t) { var i = toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; } //#endregion //#region \0@oxc-project+runtime@0.115.0/helpers/defineProperty.js function _defineProperty(e, r, t) { return (r = toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; } //#endregion //#region src/cssTokenizer.ts const between = function(num, first, last) { return num >= first && num <= last; }; function digit(code) { return between(code, 48, 57); } function hexdigit(code) { return digit(code) || between(code, 65, 70) || between(code, 97, 102); } function uppercaseletter(code) { return between(code, 65, 90); } function lowercaseletter(code) { return between(code, 97, 122); } function letter(code) { return uppercaseletter(code) || lowercaseletter(code); } function nonascii(code) { return code >= 128; } function namestartchar(code) { return letter(code) || nonascii(code) || code === 95; } function namechar(code) { return namestartchar(code) || digit(code) || code === 45; } function nonprintable(code) { return between(code, 0, 8) || code === 11 || between(code, 14, 31) || code === 127; } function newline(code) { return code === 10; } function whitespace(code) { return newline(code) || code === 9 || code === 32; } const maximumallowedcodepoint = 1114111; var InvalidCharacterError = class extends Error { constructor(message) { super(message); this.name = "InvalidCharacterError"; } }; function preprocess(str) { const codepoints = []; for (let i = 0; i < str.length; i++) { let code = str.charCodeAt(i); if (code === 13 && str.charCodeAt(i + 1) === 10) { code = 10; i++; } if (code === 13 || code === 12) code = 10; if (code === 0) code = 65533; if (between(code, 55296, 56319) && between(str.charCodeAt(i + 1), 56320, 57343)) { const lead = code - 55296; const trail = str.charCodeAt(i + 1) - 56320; code = 2 ** 16 + lead * 2 ** 10 + trail; i++; } codepoints.push(code); } return codepoints; } function stringFromCode(code) { if (code <= 65535) return String.fromCharCode(code); code -= 2 ** 16; const lead = Math.floor(code / 2 ** 10) + 55296; const trail = code % 2 ** 10 + 56320; return String.fromCharCode(lead) + String.fromCharCode(trail); } function tokenize(str1) { const str = preprocess(str1); let i = -1; const tokens = []; let code; let line = 0; let column = 0; let lastLineLength = 0; const incrLineno = function() { line += 1; lastLineLength = column; column = 0; }; const locStart = { line, column }; const codepoint = function(i) { if (i >= str.length) return -1; return str[i]; }; const next = function(num) { if (num === void 0) num = 1; if (num > 3) throw "Spec Error: no more than three codepoints of lookahead."; return codepoint(i + num); }; const consume = function(num) { if (num === void 0) num = 1; i += num; code = codepoint(i); if (newline(code)) incrLineno(); else column += num; return true; }; const reconsume = function() { i -= 1; if (newline(code)) { line -= 1; column = lastLineLength; } else column -= 1; locStart.line = line; locStart.column = column; return true; }; const eof = function(codepoint) { if (codepoint === void 0) codepoint = code; return codepoint === -1; }; const donothing = function() {}; const parseerror = function() {}; const consumeAToken = function() { consumeComments(); consume(); if (whitespace(code)) { while (whitespace(next())) consume(); return new WhitespaceToken(); } else if (code === 34) return consumeAStringToken(); else if (code === 35) if (namechar(next()) || areAValidEscape(next(1), next(2))) { const token = new HashToken(""); if (wouldStartAnIdentifier(next(1), next(2), next(3))) token.type = "id"; token.value = consumeAName(); return token; } else return new DelimToken(code); else if (code === 36) if (next() === 61) { consume(); return new SuffixMatchToken(); } else return new DelimToken(code); else if (code === 39) return consumeAStringToken(); else if (code === 40) return new OpenParenToken(); else if (code === 41) return new CloseParenToken(); else if (code === 42) if (next() === 61) { consume(); return new SubstringMatchToken(); } else return new DelimToken(code); else if (code === 43) if (startsWithANumber()) { reconsume(); return consumeANumericToken(); } else return new DelimToken(code); else if (code === 44) return new CommaToken(); else if (code === 45) if (startsWithANumber()) { reconsume(); return consumeANumericToken(); } else if (next(1) === 45 && next(2) === 62) { consume(2); return new CDCToken(); } else if (startsWithAnIdentifier()) { reconsume(); return consumeAnIdentlikeToken(); } else return new DelimToken(code); else if (code === 46) if (startsWithANumber()) { reconsume(); return consumeANumericToken(); } else return new DelimToken(code); else if (code === 58) return new ColonToken(); else if (code === 59) return new SemicolonToken(); else if (code === 60) if (next(1) === 33 && next(2) === 45 && next(3) === 45) { consume(3); return new CDOToken(); } else return new DelimToken(code); else if (code === 64) if (wouldStartAnIdentifier(next(1), next(2), next(3))) return new AtKeywordToken(consumeAName()); else return new DelimToken(code); else if (code === 91) return new OpenSquareToken(); else if (code === 92) if (startsWithAValidEscape()) { reconsume(); return consumeAnIdentlikeToken(); } else { parseerror(); return new DelimToken(code); } else if (code === 93) return new CloseSquareToken(); else if (code === 94) if (next() === 61) { consume(); return new PrefixMatchToken(); } else return new DelimToken(code); else if (code === 123) return new OpenCurlyToken(); else if (code === 124) if (next() === 61) { consume(); return new DashMatchToken(); } else if (next() === 124) { consume(); return new ColumnToken(); } else return new DelimToken(code); else if (code === 125) return new CloseCurlyToken(); else if (code === 126) if (next() === 61) { consume(); return new IncludeMatchToken(); } else return new DelimToken(code); else if (digit(code)) { reconsume(); return consumeANumericToken(); } else if (namestartchar(code)) { reconsume(); return consumeAnIdentlikeToken(); } else if (eof()) return new EOFToken(); else return new DelimToken(code); }; const consumeComments = function() { while (next(1) === 47 && next(2) === 42) { consume(2); while (true) { consume(); if (code === 42 && next() === 47) { consume(); break; } else if (eof()) { parseerror(); return; } } } }; const consumeANumericToken = function() { const num = consumeANumber(); if (wouldStartAnIdentifier(next(1), next(2), next(3))) { const token = new DimensionToken(); token.value = num.value; token.repr = num.repr; token.type = num.type; token.unit = consumeAName(); return token; } else if (next() === 37) { consume(); const token = new PercentageToken(); token.value = num.value; token.repr = num.repr; return token; } else { const token = new NumberToken(); token.value = num.value; token.repr = num.repr; token.type = num.type; return token; } }; const consumeAnIdentlikeToken = function() { const str = consumeAName(); if (str.toLowerCase() === "url" && next() === 40) { consume(); while (whitespace(next(1)) && whitespace(next(2))) consume(); if (next() === 34 || next() === 39) return new FunctionToken(str); else if (whitespace(next()) && (next(2) === 34 || next(2) === 39)) return new FunctionToken(str); else return consumeAURLToken(); } else if (next() === 40) { consume(); return new FunctionToken(str); } else return new IdentToken(str); }; const consumeAStringToken = function(endingCodePoint) { if (endingCodePoint === void 0) endingCodePoint = code; let string = ""; while (consume()) if (code === endingCodePoint || eof()) return new StringToken(string); else if (newline(code)) { parseerror(); reconsume(); return new BadStringToken(); } else if (code === 92) if (eof(next())) donothing(); else if (newline(next())) consume(); else string += stringFromCode(consumeEscape()); else string += stringFromCode(code); throw new Error("Internal error"); }; const consumeAURLToken = function() { const token = new URLToken(""); while (whitespace(next())) consume(); if (eof(next())) return token; while (consume()) if (code === 41 || eof()) return token; else if (whitespace(code)) { while (whitespace(next())) consume(); if (next() === 41 || eof(next())) { consume(); return token; } else { consumeTheRemnantsOfABadURL(); return new BadURLToken(); } } else if (code === 34 || code === 39 || code === 40 || nonprintable(code)) { parseerror(); consumeTheRemnantsOfABadURL(); return new BadURLToken(); } else if (code === 92) if (startsWithAValidEscape()) token.value += stringFromCode(consumeEscape()); else { parseerror(); consumeTheRemnantsOfABadURL(); return new BadURLToken(); } else token.value += stringFromCode(code); throw new Error("Internal error"); }; const consumeEscape = function() { consume(); if (hexdigit(code)) { const digits = [code]; for (let total = 0; total < 5; total++) if (hexdigit(next())) { consume(); digits.push(code); } else break; if (whitespace(next())) consume(); let value = Number.parseInt(digits.map((x) => { return String.fromCharCode(x); }).join(""), 16); if (value > maximumallowedcodepoint) value = 65533; return value; } else if (eof()) return 65533; else return code; }; const areAValidEscape = function(c1, c2) { if (c1 !== 92) return false; if (newline(c2)) return false; return true; }; const startsWithAValidEscape = function() { return areAValidEscape(code, next()); }; const wouldStartAnIdentifier = function(c1, c2, c3) { if (c1 === 45) return namestartchar(c2) || c2 === 45 || areAValidEscape(c2, c3); else if (namestartchar(c1)) return true; else if (c1 === 92) return areAValidEscape(c1, c2); else return false; }; const startsWithAnIdentifier = function() { return wouldStartAnIdentifier(code, next(1), next(2)); }; const wouldStartANumber = function(c1, c2, c3) { if (c1 === 43 || c1 === 45) { if (digit(c2)) return true; if (c2 === 46 && digit(c3)) return true; return false; } else if (c1 === 46) { if (digit(c2)) return true; return false; } else if (digit(c1)) return true; else return false; }; const startsWithANumber = function() { return wouldStartANumber(code, next(1), next(2)); }; const consumeAName = function() { let result = ""; while (consume()) if (namechar(code)) result += stringFromCode(code); else if (startsWithAValidEscape()) result += stringFromCode(consumeEscape()); else { reconsume(); return result; } throw new Error("Internal parse error"); }; const consumeANumber = function() { let repr = ""; let type = "integer"; if (next() === 43 || next() === 45) { consume(); repr += stringFromCode(code); } while (digit(next())) { consume(); repr += stringFromCode(code); } if (next(1) === 46 && digit(next(2))) { consume(); repr += stringFromCode(code); consume(); repr += stringFromCode(code); type = "number"; while (digit(next())) { consume(); repr += stringFromCode(code); } } const c1 = next(1); const c2 = next(2); const c3 = next(3); if ((c1 === 69 || c1 === 101) && digit(c2)) { consume(); repr += stringFromCode(code); consume(); repr += stringFromCode(code); type = "number"; while (digit(next())) { consume(); repr += stringFromCode(code); } } else if ((c1 === 69 || c1 === 101) && (c2 === 43 || c2 === 45) && digit(c3)) { consume(); repr += stringFromCode(code); consume(); repr += stringFromCode(code); consume(); repr += stringFromCode(code); type = "number"; while (digit(next())) { consume(); repr += stringFromCode(code); } } const value = convertAStringToANumber(repr); return { type, value, repr }; }; const convertAStringToANumber = function(string) { return +string; }; const consumeTheRemnantsOfABadURL = function() { while (consume()) if (code === 41 || eof()) return; else if (startsWithAValidEscape()) { consumeEscape(); donothing(); } else donothing(); }; let iterationCount = 0; while (!eof(next())) { tokens.push(consumeAToken()); iterationCount++; if (iterationCount > str.length * 2) throw new Error("I'm infinite-looping!"); } return tokens; } var CSSParserToken = class { constructor() { _defineProperty(this, "tokenType", ""); _defineProperty(this, "value", void 0); } toJSON() { return { token: this.tokenType }; } toString() { return this.tokenType; } toSource() { return `${this}`; } }; var BadStringToken = class extends CSSParserToken { constructor(..._args) { super(..._args); _defineProperty(this, "tokenType", "BADSTRING"); } }; var BadURLToken = class extends CSSParserToken { constructor(..._args2) { super(..._args2); _defineProperty(this, "tokenType", "BADURL"); } }; var WhitespaceToken = class extends CSSParserToken { constructor(..._args3) { super(..._args3); _defineProperty(this, "tokenType", "WHITESPACE"); } toString() { return "WS"; } toSource() { return " "; } }; var CDOToken = class extends CSSParserToken { constructor(..._args4) { super(..._args4); _defineProperty(this, "tokenType", "CDO"); } toSource() { return "<!--"; } }; var CDCToken = class extends CSSParserToken { constructor(..._args5) { super(..._args5); _defineProperty(this, "tokenType", "CDC"); } toSource() { return "-->"; } }; var ColonToken = class extends CSSParserToken { constructor(..._args6) { super(..._args6); _defineProperty(this, "tokenType", ":"); } }; var SemicolonToken = class extends CSSParserToken { constructor(..._args7) { super(..._args7); _defineProperty(this, "tokenType", ";"); } }; var CommaToken = class extends CSSParserToken { constructor(..._args8) { super(..._args8); _defineProperty(this, "tokenType", ","); } }; var GroupingToken = class extends CSSParserToken { constructor(..._args9) { super(..._args9); _defineProperty(this, "value", ""); _defineProperty(this, "mirror", ""); } }; var OpenCurlyToken = class extends GroupingToken { constructor() { super(); _defineProperty(this, "tokenType", "{"); this.value = "{"; this.mirror = "}"; } }; var CloseCurlyToken = class extends GroupingToken { constructor() { super(); _defineProperty(this, "tokenType", "}"); this.value = "}"; this.mirror = "{"; } }; var OpenSquareToken = class extends GroupingToken { constructor() { super(); _defineProperty(this, "tokenType", "["); this.value = "["; this.mirror = "]"; } }; var CloseSquareToken = class extends GroupingToken { constructor() { super(); _defineProperty(this, "tokenType", "]"); this.value = "]"; this.mirror = "["; } }; var OpenParenToken = class extends GroupingToken { constructor() { super(); _defineProperty(this, "tokenType", "("); this.value = "("; this.mirror = ")"; } }; var CloseParenToken = class extends GroupingToken { constructor() { super(); _defineProperty(this, "tokenType", ")"); this.value = ")"; this.mirror = "("; } }; var IncludeMatchToken = class extends CSSParserToken { constructor(..._args10) { super(..._args10); _defineProperty(this, "tokenType", "~="); } }; var DashMatchToken = class extends CSSParserToken { constructor(..._args11) { super(..._args11); _defineProperty(this, "tokenType", "|="); } }; var PrefixMatchToken = class extends CSSParserToken { constructor(..._args12) { super(..._args12); _defineProperty(this, "tokenType", "^="); } }; var SuffixMatchToken = class extends CSSParserToken { constructor(..._args13) { super(..._args13); _defineProperty(this, "tokenType", "$="); } }; var SubstringMatchToken = class extends CSSParserToken { constructor(..._args14) { super(..._args14); _defineProperty(this, "tokenType", "*="); } }; var ColumnToken = class extends CSSParserToken { constructor(..._args15) { super(..._args15); _defineProperty(this, "tokenType", "||"); } }; var EOFToken = class extends CSSParserToken { constructor(..._args16) { super(..._args16); _defineProperty(this, "tokenType", "EOF"); } toSource() { return ""; } }; var DelimToken = class extends CSSParserToken { constructor(code) { super(); _defineProperty(this, "tokenType", "DELIM"); _defineProperty(this, "value", ""); this.value = stringFromCode(code); } toString() { return `DELIM(${this.value})`; } toJSON() { const json = this.constructor.prototype.constructor.prototype.toJSON.call(this); json.value = this.value; return json; } toSource() { if (this.value === "\\") return "\\\n"; else return this.value; } }; var StringValuedToken = class extends CSSParserToken { constructor(..._args17) { super(..._args17); _defineProperty(this, "value", ""); } ASCIIMatch(str) { return this.value.toLowerCase() === str.toLowerCase(); } toJSON() { const json = this.constructor.prototype.constructor.prototype.toJSON.call(this); json.value = this.value; return json; } }; var IdentToken = class extends StringValuedToken { constructor(val) { super(); _defineProperty(this, "tokenType", "IDENT"); this.value = val; } toString() { return `IDENT(${this.value})`; } toSource() { return escapeIdent(this.value); } }; var FunctionToken = class extends StringValuedToken { constructor(val) { super(); _defineProperty(this, "tokenType", "FUNCTION"); _defineProperty(this, "mirror", void 0); this.value = val; this.mirror = ")"; } toString() { return `FUNCTION(${this.value})`; } toSource() { return `${escapeIdent(this.value)}(`; } }; var AtKeywordToken = class extends StringValuedToken { constructor(val) { super(); _defineProperty(this, "tokenType", "AT-KEYWORD"); this.value = val; } toString() { return `AT(${this.value})`; } toSource() { return `@${escapeIdent(this.value)}`; } }; var HashToken = class extends StringValuedToken { constructor(val) { super(); _defineProperty(this, "tokenType", "HASH"); _defineProperty(this, "type", void 0); this.value = val; this.type = "unrestricted"; } toString() { return `HASH(${this.value})`; } toJSON() { const json = this.constructor.prototype.constructor.prototype.toJSON.call(this); json.value = this.value; json.type = this.type; return json; } toSource() { if (this.type === "id") return `#${escapeIdent(this.value)}`; else return `#${escapeHash(this.value)}`; } }; var StringToken = class extends StringValuedToken { constructor(val) { super(); _defineProperty(this, "tokenType", "STRING"); this.value = val; } toString() { return `"${escapeString(this.value)}"`; } }; var URLToken = class extends StringValuedToken { constructor(val) { super(); _defineProperty(this, "tokenType", "URL"); this.value = val; } toString() { return `URL(${this.value})`; } toSource() { return `url("${escapeString(this.value)}")`; } }; var NumberToken = class extends CSSParserToken { constructor() { super(); _defineProperty(this, "tokenType", "NUMBER"); _defineProperty(this, "type", void 0); _defineProperty(this, "repr", void 0); this.type = "integer"; this.repr = ""; } toString() { if (this.type === "integer") return `INT(${this.value})`; return `NUMBER(${this.value})`; } toJSON() { const json = super.toJSON(); json.value = this.value; json.type = this.type; json.repr = this.repr; return json; } toSource() { return this.repr; } }; var PercentageToken = class extends CSSParserToken { constructor() { super(); _defineProperty(this, "tokenType", "PERCENTAGE"); _defineProperty(this, "repr", void 0); this.repr = ""; } toString() { return `PERCENTAGE(${this.value})`; } toJSON() { const json = this.constructor.prototype.constructor.prototype.toJSON.call(this); json.value = this.value; json.repr = this.repr; return json; } toSource() { return `${this.repr}%`; } }; var DimensionToken = class extends CSSParserToken { constructor() { super(); _defineProperty(this, "tokenType", "DIMENSION"); _defineProperty(this, "type", void 0); _defineProperty(this, "repr", void 0); _defineProperty(this, "unit", void 0); this.type = "integer"; this.repr = ""; this.unit = ""; } toString() { return `DIM(${this.value},${this.unit})`; } toJSON() { const json = this.constructor.prototype.constructor.prototype.toJSON.call(this); json.value = this.value; json.type = this.type; json.repr = this.repr; json.unit = this.unit; return json; } toSource() { const source = this.repr; let unit = escapeIdent(this.unit); if (unit[0].toLowerCase() === "e" && (unit[1] === "-" || between(unit.charCodeAt(1), 48, 57))) unit = `\\65 ${unit.slice(1, unit.length)}`; return source + unit; } }; function escapeIdent(string) { string = `${string}`; let result = ""; const firstcode = string.charCodeAt(0); for (let i = 0; i < string.length; i++) { const code = string.charCodeAt(i); if (code === 0) throw new InvalidCharacterError("Invalid character: the input contains U+0000."); if (between(code, 1, 31) || code === 127 || i === 0 && between(code, 48, 57) || i === 1 && between(code, 48, 57) && firstcode === 45) result += `\\${code.toString(16)} `; else if (code >= 128 || code === 45 || code === 95 || between(code, 48, 57) || between(code, 65, 90) || between(code, 97, 122)) result += string[i]; else result += `\\${string[i]}`; } return result; } function escapeHash(string) { string = `${string}`; let result = ""; for (let i = 0; i < string.length; i++) { const code = string.charCodeAt(i); if (code === 0) throw new InvalidCharacterError("Invalid character: the input contains U+0000."); if (code >= 128 || code === 45 || code === 95 || between(code, 48, 57) || between(code, 65, 90) || between(code, 97, 122)) result += string[i]; else result += `\\${code.toString(16)} `; } return result; } function escapeString(string) { string = `${string}`; let result = ""; for (let i = 0; i < string.length; i++) { const code = string.charCodeAt(i); if (code === 0) throw new InvalidCharacterError("Invalid character: the input contains U+0000."); if (between(code, 1, 31) || code === 127) result += `\\${code.toString(16)} `; else if (code === 34 || code === 92) result += `\\${string[i]}`; else result += string[i]; } return result; } //#endregion //#region src/cssParser.ts /** * Copyright (c) Microsoft Corporation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ var InvalidSelectorError = class extends Error {}; function parseCSS(selector, customNames) { let tokens; try { tokens = tokenize(selector); if (!(tokens[tokens.length - 1] instanceof EOFToken)) tokens.push(new EOFToken()); } catch (e) { const newMessage = `${e.message} while parsing selector "${selector}"`; const index = (e.stack || "").indexOf(e.message); if (index !== -1) e.stack = e.stack.substring(0, index) + newMessage + e.stack.substring(index + e.message.length); e.message = newMessage; throw e; } const unsupportedToken = tokens.find((token) => { 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; }); if (unsupportedToken) throw new InvalidSelectorError(`Unsupported token "${unsupportedToken.toSource()}" while parsing selector "${selector}"`); let pos = 0; const names = /* @__PURE__ */ new Set(); function unexpected() { return new InvalidSelectorError(`Unexpected token "${tokens[pos].toSource()}" while parsing selector "${selector}"`); } function skipWhitespace() { while (tokens[pos] instanceof WhitespaceToken) pos++; } function isIdent(p = pos) { return tokens[p] instanceof IdentToken; } function isString(p = pos) { return tokens[p] instanceof StringToken; } function isNumber(p = pos) { return tokens[p] instanceof NumberToken; } function isComma(p = pos) { return tokens[p] instanceof CommaToken; } function isOpenParen(p = pos) { return tokens[p] instanceof OpenParenToken; } function isCloseParen(p = pos) { return tokens[p] instanceof CloseParenToken; } function isFunction(p = pos) { return tokens[p] instanceof FunctionToken; } function isStar(p = pos) { return tokens[p] instanceof DelimToken && tokens[p].value === "*"; } function isEOF(p = pos) { return tokens[p] instanceof EOFToken; } function isClauseCombinator(p = pos) { return tokens[p] instanceof DelimToken && [ ">", "+", "~" ].includes(tokens[p].value); } function isSelectorClauseEnd(p = pos) { return isComma(p) || isCloseParen(p) || isEOF(p) || isClauseCombinator(p) || tokens[p] instanceof WhitespaceToken; } function consumeFunctionArguments() { const result = [consumeArgument()]; while (true) { skipWhitespace(); if (!isComma()) break; pos++; result.push(consumeArgument()); } return result; } function consumeArgument() { skipWhitespace(); if (isNumber()) return tokens[pos++].value; if (isString()) return tokens[pos++].value; return consumeComplexSelector(); } function consumeComplexSelector() { const result = { simples: [] }; skipWhitespace(); if (isClauseCombinator()) result.simples.push({ selector: { functions: [{ name: "scope", args: [] }] }, combinator: "" }); else result.simples.push({ selector: consumeSimpleSelector(), combinator: "" }); while (true) { skipWhitespace(); if (isClauseCombinator()) { result.simples[result.simples.length - 1].combinator = tokens[pos++].value; skipWhitespace(); } else if (isSelectorClauseEnd()) break; result.simples.push({ combinator: "", selector: consumeSimpleSelector() }); } return result; } function consumeSimpleSelector() { let rawCSSString = ""; const functions = []; while (!isSelectorClauseEnd()) if (isIdent() || isStar()) rawCSSString += tokens[pos++].toSource(); else if (tokens[pos] instanceof HashToken) rawCSSString += tokens[pos++].toSource(); else if (tokens[pos] instanceof DelimToken && tokens[pos].value === ".") { pos++; if (isIdent()) rawCSSString += `.${tokens[pos++].toSource()}`; else throw unexpected(); } else if (tokens[pos] instanceof ColonToken) { pos++; if (isIdent()) if (!customNames.has(tokens[pos].value.toLowerCase())) rawCSSString += `:${tokens[pos++].toSource()}`; else { const name = tokens[pos++].value.toLowerCase(); functions.push({ name, args: [] }); names.add(name); } else if (isFunction()) { const name = tokens[pos++].value.toLowerCase(); if (!customNames.has(name)) rawCSSString += `:${name}(${consumeBuiltinFunctionArguments()})`; else { functions.push({ name, args: consumeFunctionArguments() }); names.add(name); } skipWhitespace(); if (!isCloseParen()) throw unexpected(); pos++; } else throw unexpected(); } else if (tokens[pos] instanceof OpenSquareToken) { rawCSSString += "["; pos++; while (!(tokens[pos] instanceof CloseSquareToken) && !isEOF()) rawCSSString += tokens[pos++].toSource(); if (!(tokens[pos] instanceof CloseSquareToken)) throw unexpected(); rawCSSString += "]"; pos++; } else throw unexpected(); if (!rawCSSString && !functions.length) throw unexpected(); return { css: rawCSSString || void 0, functions }; } function consumeBuiltinFunctionArguments() { let s = ""; let balance = 1; while (!isEOF()) { if (isOpenParen() || isFunction()) balance++; if (isCloseParen()) balance--; if (!balance) break; s += tokens[pos++].toSource(); } return s; } const result = consumeFunctionArguments(); if (!isEOF()) throw unexpected(); if (result.some((arg) => typeof arg !== "object" || !("simples" in arg))) throw new InvalidSelectorError(`Error while parsing selector "${selector}"`); return { selector: result, names: Array.from(names) }; } //#endregion //#region src/selectorParser.ts const kNestedSelectorNames = new Set([ "internal:has", "internal:has-not", "internal:and", "internal:or", "internal:chain", "left-of", "right-of", "above", "below", "near" ]); const kNestedSelectorNamesWithDistance = new Set([ "left-of", "right-of", "above", "below", "near" ]); const customCSSNames = 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" ]); function parseSelector(selector) { const parsedStrings = parseSelectorString(selector); const parts = []; for (const part of parsedStrings.parts) { if (part.name === "css" || part.name === "css:light") { if (part.name === "css:light") part.body = `:light(${part.body})`; const parsedCSS = parseCSS(part.body, customCSSNames); parts.push({ name: "css", body: parsedCSS.selector, source: part.body }); continue; } if (kNestedSelectorNames.has(part.name)) { let innerSelector; let distance; try { const unescaped = JSON.parse(`[${part.body}]`); if (!Array.isArray(unescaped) || unescaped.length < 1 || unescaped.length > 2 || typeof unescaped[0] !== "string") throw new InvalidSelectorError(`Malformed selector: ${part.name}=${part.body}`); innerSelector = unescaped[0]; if (unescaped.length === 2) { if (typeof unescaped[1] !== "number" || !kNestedSelectorNamesWithDistance.has(part.name)) throw new InvalidSelectorError(`Malformed selector: ${part.name}=${part.body}`); distance = unescaped[1]; } } catch (e) { throw new InvalidSelectorError(`Malformed selector: ${part.name}=${part.body}`); } const nested = { name: part.name, source: part.body, body: { parsed: parseSelector(innerSelector), distance } }; const lastFrame = [...nested.body.parsed.parts].reverse().find((part) => part.name === "internal:control" && part.body === "enter-frame"); const lastFrameIndex = lastFrame ? nested.body.parsed.parts.indexOf(lastFrame) : -1; if (lastFrameIndex !== -1 && selectorPartsEqual(nested.body.parsed.parts.slice(0, lastFrameIndex + 1), parts.slice(0, lastFrameIndex + 1))) nested.body.parsed.parts.splice(0, lastFrameIndex + 1); parts.push(nested); continue; } parts.push({ ...part, source: part.body }); } if (kNestedSelectorNames.has(parts[0].name)) throw new InvalidSelectorError(`"${parts[0].name}" selector cannot be first`); return { capture: parsedStrings.capture, parts }; } function selectorPartsEqual(list1, list2) { return stringifySelector({ parts: list1 }) === stringifySelector({ parts: list2 }); } function stringifySelector(selector, forceEngineName) { if (typeof selector === "string") return selector; return selector.parts.map((p, i) => { let includeEngine = true; if (!forceEngineName && i !== selector.capture) { if (p.name === "css") includeEngine = false; else if (p.name === "xpath" && p.source.startsWith("//") || p.source.startsWith("..")) includeEngine = false; } const prefix = includeEngine ? `${p.name}=` : ""; return `${i === selector.capture ? "*" : ""}${prefix}${p.source}`; }).join(" >> "); } function visitAllSelectorParts(selector, visitor) { const visit = (selector, nested) => { for (const part of selector.parts) { visitor(part, nested); if (kNestedSelectorNames.has(part.name)) visit(part.body.parsed, true); } }; visit(selector, false); } function parseSelectorString(selector) { let index = 0; let quote; let start = 0; const result = { parts: [] }; const append = () => { const part = selector.substring(start, index).trim(); const eqIndex = part.indexOf("="); let name; let body; if (eqIndex !== -1 && part.substring(0, eqIndex).trim().match(/^[\w\-+:*]+$/)) { name = part.substring(0, eqIndex).trim(); body = part.substring(eqIndex + 1); } else if (part.length > 1 && part[0] === "\"" && part[part.length - 1] === "\"") { name = "text"; body = part; } else if (part.length > 1 && part[0] === "'" && part[part.length - 1] === "'") { name = "text"; body = part; } else if (/^\(*\/\//.test(part) || part.startsWith("..")) { name = "xpath"; body = part; } else { name = "css"; body = part; } let capture = false; if (name[0] === "*") { capture = true; name = name.substring(1); } result.parts.push({ name, body }); if (capture) { if (result.capture !== void 0) throw new InvalidSelectorError(`Only one of the selectors can capture using * modifier`); result.capture = result.parts.length - 1; } }; if (!selector.includes(">>")) { index = selector.length; append(); return result; } const shouldIgnoreTextSelectorQuote = () => { const match = selector.substring(start, index).match(/^\s*text\s*=(.*)$/); return !!match && !!match[1]; }; while (index < selector.length) { const c = selector[index]; if (c === "\\" && index + 1 < selector.length) index += 2; else if (c === quote) { quote = void 0; index++; } else if (!quote && (c === "\"" || c === "'" || c === "`") && !shouldIgnoreTextSelectorQuote()) { quote = c; index++; } else if (!quote && c === ">" && selector[index + 1] === ">") { append(); index += 2; start = index; } else index++; } append(); return result; } function parseAttributeSelector(selector, allowUnquotedStrings) { let wp = 0; let EOL = selector.length === 0; const next = () => selector[wp] || ""; const eat1 = () => { const result = next(); ++wp; EOL = wp >= selector.length; return result; }; const syntaxError = (stage) => { if (EOL) throw new InvalidSelectorError(`Unexpected end of selector while parsing selector \`${selector}\``); throw new InvalidSelectorError(`Error while parsing selector \`${selector}\` - unexpected symbol "${next()}" at position ${wp}${stage ? ` during ${stage}` : ""}`); }; function skipSpaces() { while (!EOL && /\s/.test(next())) eat1(); } function isCSSNameChar(char) { return char >= "€" || char >= "0" && char <= "9" || char >= "A" && char <= "Z" || char >= "a" && char <= "z" || char >= "0" && char <= "9" || char === "_" || char === "-"; } function readIdentifier() { let result = ""; skipSpaces(); while (!EOL && isCSSNameChar(next())) result += eat1(); return result; } function readQuotedString(quote) { let result = eat1(); if (result !== quote) syntaxError("parsing quoted string"); while (!EOL && next() !== quote) { if (next() === "\\") eat1(); result += eat1(); } if (next() !== quote) syntaxError("parsing quoted string"); result += eat1(); return result; } function readRegularExpression() { if (eat1() !== "/") syntaxError("parsing regular expression"); let source = ""; let inClass = false; while (!EOL) { if (next() === "\\") { source += eat1(); if (EOL) syntaxError("parsing regular expression"); } else if (inClass && next() === "]") inClass = false; else if (!inClass && next() === "[") inClass = true; else if (!inClass && next() === "/") break; source += eat1(); } if (eat1() !== "/") syntaxError("parsing regular expression"); let flags = ""; while (!EOL && next().match(/[dgimsuy]/)) flags += eat1(); try { return new RegExp(source, flags); } catch (e) { throw new InvalidSelectorError(`Error while parsing selector \`${selector}\`: ${e.message}`); } } function readAttributeToken() { let token = ""; skipSpaces(); if (next() === `'` || next() === `"`) token = readQuotedString(next()).slice(1, -1); else token = readIdentifier(); if (!token) syntaxError("parsing property path"); return token; } function readOperator() { skipSpaces(); let op = ""; if (!EOL) op += eat1(); if (!EOL && op !== "=") op += eat1(); if (![ "=", "*=", "^=", "$=", "|=", "~=" ].includes(op)) syntaxError("parsing operator"); return op; } function readAttribute() { eat1(); const jsonPath = []; jsonPath.push(readAttributeToken()); skipSpaces(); while (next() === ".") { eat1(); jsonPath.push(readAttributeToken()); skipSpaces(); } if (next() === "]") { eat1(); return { name: jsonPath.join("."), jsonPath, op: "<truthy>", value: null, caseSensitive: false }; } const operator = readOperator(); let value; let caseSensitive = true; skipSpaces(); if (next() === "/") { if (operator !== "=") throw new InvalidSelectorError(`Error while parsing selector \`${selector}\` - cannot use ${operator} in attribute with regular expression`); value = readRegularExpression(); } else if (next() === `'` || next() === `"`) { value = readQuotedString(next()).slice(1, -1); skipSpaces(); if (next() === "i" || next() === "I") { caseSensitive = false; eat1(); } else if (next() === "s" || next() === "S") { caseSensitive = true; eat1(); } } else { value = ""; while (!EOL && (isCSSNameChar(next()) || next() === "+" || next() === ".")) value += eat1(); if (value === "true") value = true; else if (value === "false") value = false; else if (!allowUnquotedStrings) { value = +value; if (Number.isNaN(value)) syntaxError("parsing attribute value"); } } skipSpaces(); if (next() !== "]") syntaxError("parsing attribute value"); eat1(); if (operator !== "=" && typeof value !== "string") throw new InvalidSelectorError(`Error while parsing selector \`${selector}\` - cannot use ${operator} in attribute with non-string matching value - ${value}`); return { name: jsonPath.join("."), jsonPath, op: operator, value, caseSensitive }; } const result = { name: "", attributes: [] }; result.name = readIdentifier(); skipSpaces(); while (next() === "[") { result.attributes.push(readAttribute()); skipSpaces(); } if (!EOL) syntaxError(void 0); if (!result.name && !result.attributes.length) throw new InvalidSelectorError(`Error while parsing selector \`${selector}\` - selector cannot be empty`); return result; } //#endregion //#region src/stringUtils.ts /** * Copyright (c) Microsoft Corporation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ function escapeWithQuotes(text, char = "'") { const stringified = JSON.stringify(text); const escapedText = stringified.substring(1, stringified.length - 1).replace(/\\"/g, "\""); if (char === "'") return char + escapedText.replace(/'/g, "\\'") + char; if (char === "\"") return char + escapedText.replace(/"/g, "\\\"") + char; if (char === "`") return char + escapedText.replace(/`/g, "`") + char; throw new Error("Invalid escape char"); } function cssEscape(s) { let result = ""; for (let i = 0; i < s.length; i++) result += cssEscapeOne(s, i); return result; } function quoteCSSAttributeValue(text) { return `"${cssEscape(text).replace(/\\ /g, " ")}"`; } function cssEscapeOne(s, i) { const c = s.charCodeAt(i); if (c === 0) return "�"; if (c >= 1 && c <= 31 || c >= 48 && c <= 57 && (i === 0 || i === 1 && s.charCodeAt(0) === 45)) return `\\${c.toString(16)} `; if (i === 0 && c === 45 && s.length === 1) return `\\${s.charAt(i)}`; if (c >= 128 || c === 45 || c === 95 || c >= 48 && c <= 57 || c >= 65 && c <= 90 || c >= 97 && c <= 122) return s.charAt(i); return `\\${s.charAt(i)}`; } function normalizeWhiteSpace(text) { return text.replace(/[\u200b\u00ad]/g, "").trim().replace(/\s+/g, " "); } function normalizeEscapedRegexQuotes(source) { return source.replace(/(^|[^\\])(\\\\)*\\(['"`])/g, "$1$2$3"); } function escapeRegexForSelector(re) { if (re.unicode || re.unicodeSets) return String(re); return String(re).replace(/(^|[^\\])(\\\\)*(["'`])/g, "$1$2\\$3").replace(/>>/g, "\\>\\>"); } function escapeForTextSelector(text, exact) { if (typeof text !== "string") return escapeRegexForSelector(text); return `${JSON.stringify(text)}${exact ? "s" : "i"}`; } function escapeForAttributeSelector(value, exact) { if (typeof value !== "string") return escapeRegexForSelector(value); return `"${value.replace(/\\/g, "\\\\").replace(/"/g, "\\\"")}"${exact ? "s" : "i"}`; } function trimString(input, cap, suffix = "") { if (input.length <= cap) return input; const chars = [...input]; if (chars.length > cap) return chars.slice(0, cap - suffix.length).join("") + suffix; return chars.join(""); } function trimStringWithEllipsis(input, cap) { return trimString(input, cap, "…"); } function escapeRegExp(s) { return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); } //#endregion //#region src/domUtils.ts /** * Copyright (c) Microsoft Corporation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ function parentElementOrShadowHost(element) { if (element.parentElement) return element.parentElement; if (!element.parentNode) return; if (element.parentNode.nodeType === 11 && element.parentNode.host) return element.parentNode.host; } function enclosingShadowRootOrDocument(element) { let node = element; while (node.parentNode) node = node.parentNode; if (node.nodeType === 11 || node.nodeType === 9) return node; } function enclosingShadowHost(element) { while (element.parentElement) element = element.parentElement; return parentElementOrShadowHost(element); } function closestCrossShadow(element, css) { while (element) { const closest = element.closest(css); if (closest) return closest; element = enclosingShadowHost(element); } } function getElementComputedStyle(element, pseudo) { return element.ownerDocument && element.ownerDocument.defaultView ? element.ownerDocument.defaultView.getComputedStyle(element, pseudo) : void 0; } function isElementStyleVisibilityVisible(element, style) { var _style; style = (_style = style) !== null && _style !== void 0 ? _style : getElementComputedStyle(element); if (!style) return true; if (Element.prototype.checkVisibility && Ivya.options.browser !== "webkit") { if (!element.checkVisibility()) return false; } else { const detailsOrSummary = element.closest("details,summary"); if (detailsOrSummary !== element && (detailsOrSummary === null || detailsOrSummary === void 0 ? void 0 : detailsOrSummary.nodeName) === "DETAILS" && !detailsOrSummary.open) return false; } if (style.visibility !== "visible") return false; return true; } function isElementVisible(element) { const style = getElementComputedStyle(element); if (!style) return true; if (style.display === "contents") { for (let child = element.firstChild; child; child = child.nextSibling) { if (child.nodeType === 1 && isElementVisible(child)) return true; if (child.nodeType === 3 && isVisibleTextNode(child)) return true; } return false; } if (!isElementStyleVisibilityVisible(element, style)) return false; const rect = element.getBoundingClientRect(); return rect.width > 0 && rect.height > 0; } function isVisibleTextNode(node) { const range = node.ownerDocument.createRange(); range.selectNode(node); const rect = range.getBoundingClientRect(); return rect.width > 0 && rect.height > 0; } function elementSafeTagName(element) { if (element instanceof HTMLFormElement) return "FORM"; return element.tagName.toUpperCase(); } //#endregion //#region src/roleUtils.ts /** * Copyright (c) Microsoft Corporation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ function hasExplicitAccessibleName(e) { return e.hasAttribute("aria-label") || e.hasAttribute("aria-labelledby"); } const 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]"; const kGlobalAriaAttributes = new Map([ ["aria-atomic", void 0], ["aria-busy", void 0], ["aria-controls", void 0], ["aria-current", void 0], ["aria-describedby", void 0], ["aria-details", void 0], ["aria-dropeffect", void 0], ["aria-flowto", void 0], ["aria-grabbed", void 0], ["aria-hidden", void 0], ["aria-keyshortcuts", void 0], ["aria-label", new Set([ "caption", "code", "deletion", "emphasis", "generic", "insertion", "paragraph", "presentation", "strong", "subscript", "superscript" ])], ["aria-labelledby", new Set([ "caption", "code", "deletion", "emphasis", "generic", "insertion", "paragraph", "presentation", "strong", "subscript", "superscript" ])], ["aria-live", void 0], ["aria-owns", void 0], ["aria-relevant", void 0], ["aria-roledescription", new Set(["generic"])] ]); function hasGlobalAriaAttribute(element, forRole) { return [...kGlobalAriaAttributes].some(([attr, prohibited]) => { return !(prohibited === null || prohibited === void 0 ? void 0 : prohibited.has(forRole || "")) && element.hasAttribute(attr); }); } function hasTabIndex(element) { return !Number.isNaN(Number(String(element.getAttribute("tabindex")))); } function isFocusable(element) { return !isNativelyDisabled(element) && (isNativelyFocusable(element) || hasTabIndex(element)); } function isNativelyFocusable(element) { const tagName = elementSafeTagName(element); if ([ "BUTTON", "DETAILS", "SELECT", "TEXTAREA" ].includes(tagName)) return true; if (tagName === "A" || tagName === "AREA") return element.hasAttribute("href"); if (tagName === "INPUT") return !element.hidden; return false; } const kImplicitRoleByTagName = { A: (e) => { return e.hasAttribute("href") ? "link" : null; }, AREA: (e) => { return e.hasAttribute("href") ? "link" : null; }, ARTICLE: () => "article", ASIDE: () => "complementary", BLOCKQUOTE: () => "blockquote", BUTTON: () => "button", CAPTION: () => "caption", CODE: () => "code", DATALIST: () => "listbox", DD: () => "definition", DEL: () => "deletion", DETAILS: () => "group", DFN: () => "term", DIALOG: () => "dialog", DT: () => "term", EM: () => "emphasis", FIELDSET: () => "group", FIGURE: () => "figure", FOOTER: (e) => closestCrossShadow(e, kAncestorPreventingLandmark) ? null : "contentinfo", FORM: (e) => hasExplicitAccessibleName(e) ? "form" : null, H1: () => "heading", H2: () => "heading", H3: () => "heading", H4: () => "heading", H5: () => "heading", H6: () => "heading", HEADER: (e) => closestCrossShadow(e, kAncestorPreventingLandmark) ? null : "banner", HR: () => "separator", HTML: () => "document", IMG: (e) => e.getAttribute("alt") === "" && !e.getAttribute("title") && !hasGlobalAriaAttribute(e) && !hasTabIndex(e) ? "presentation" : "img", INPUT